/*
 * Bibliothèque ajoutant des méthodes aux types de base de javascript
 * version 1.3 du 20/02/2011 par Lenzotti romain
 * since 1.3 
 * + Détection de la bibliothèque jQuery. Si elle existe, jQuery sera configuré en mode noConflict.
 * + Ajout de la méthode Extends.ready
 * since 1.2
 * + Ajout de l'encodage de chaine en MD5.
 * since 1.1
 * + Ajout de la classe StopEvent
 * + Ajout de la méthode observePattern
 * since 1.0 
 * . Modification des méthodes Node. Ces dernières sont ajoutés via la méthode Element.addMethods.
 * . Optimisation des méthodes Effect dans Node.
 * - Suppression de la méthode Extends::extend
 * + Ajout de la méthode format au type String et Number 
 * + Ajout de l'espace de nommage $Body référence vers document.stage.body
 * + Ajout des constantes des touche F5 à F12 à l'objet Event
 * + Ajout de la méthode getKeyCode à l'objet Event
 * + Correction d'un bug de conversion String vers Date (String#toString)
 * 
 * This work is licensed under a Creative Commons Attribution 2.5 Generic License 
 * Documentation : http://rom-makita.fr/extends/
 * Licence : http://creativecommons.org/licenses/by/2.5/
 **/
 
/*
 * ExtWinJS
 *
 *  Extends et Window JS sont deux bibliothèques apportant respectivement des outils pour le développement
 *  d'applications.
 * 
 *  #### Extends JS
 * 
 *  Cette bibliothèque ajoute toutes les méthodes de bases pour le traitement des données et le support des élements
 *  de base comme la classe [[Node]]. Extends ce découpe en deux sections :
 * 
 *  * [[lang]] : Ajout des méthodes de bases au langage Javascript
 *  * [[DOM]] : Ajout élément de base au DOM.
 *
 *  #### Window JS
 *
 *  Cette bibliothèque est un gestionnaire de fenêtre et de formualaire complet. Elle apporte tous les éléments
 *  pour concevoir une application graphique web basé sur des fenêtres et des formulaires. Elle permet donc
 *  de développer rapidement de grosse application professionnel pour le web.
 *
**/
 
/**
 * == lang ==
 * Section du langage. Cette section regroupe les classes et fonctions mises à disposition par la bibliothèque
 * Extends.
 **/

/**
 *  == DOM ==
 * Section du DOM. Cette section regroupe les classes et méthodes ajoutées au DOM JavaScript.
 **/
 
/** section: lang
 * Extends
 * Cette classe est le noyau de la bibliothèque Extends. Elle vérifie la précondition de chargement de la bibliothèque. 
 * Cette précondition et la version du prototype requis pour ce lancer (voir `Extends::REQUIRED_PROTOTYPE`).
 * Ces autres fonctions sont de s'occuper du chargement des outils externes à la bibliothèque (comme Window JS) et
 * le chargement du gestionnaire de langage Multilingue.
 **/
var Extends = {
/** 
 * Extends.Version -> String
 * Indique la version de la bibliothèque extends (read-only)
 **/
  	Version: 			'1.3',
/** 
 * Extends.REQUIRED_VERSION -> String
 * Indique la version minimal de protype.js
 **/
  	REQUIRED_PROTOTYPE: '1.6.0',
/** 
 * Extends.URL -> String
 * Url de la page en cours d'éxécution
 **/
	URL: 				'',
/** 
 * Extends.PATH -> String
 * Lien du dossier de la bibliothèque extends (accessible après chargement de la bibliothèque)
 **/
	PATH:				'',
	NODE_FPS:			100,
	NODE_DURATION:		0.5,
	callbacks:			{},
/**
 * Extends.fire(eventName) -> void
 * - eventName (String): Nom de l'événement à éxécuter.
 *
 * Execute un nom d'événement enregistré dans le gestion des événements d'Extends.
 **/
	fire: function(eventName){
		
		if(!Object.isUndefined(this.callbacks[eventName])){
			
			var properties = $A(arguments);
			var args = [];
	
			for(var i = 1; i < properties.length; i += 1){
				args.push(properties[i]);
			}
			
			for(var i = 0; i < this.callbacks[eventName].length; i+=1){
				try{
					if(Object.isFunction(this.callbacks[eventName][i])){
						this.callbacks[eventName][i].apply('', args);
					}
				}catch(er){}
			}
			
		}
	},
/**
 * Extends.load( ) -> undefined
 * Charge la bibliothèque Extends et les bibliothèques externes.
 **/
	load: function() {
		
		Extends.URL = ((""+window.location).split('#')[0]).split('?')[0];

		var file = window.location.pathname.split("/");
		Extends.URL = Extends.URL.replace(file[file.length - 1], '');
		
		function convertVersionString(versionString){
			var r = versionString.split('.');
			return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
		}
	
		if(		(typeof Prototype=='undefined') 
			|| 	(typeof Element == 'undefined') 
			|| 	(typeof Element.Methods=='undefined') 
			|| 	(convertVersionString(Prototype.Version) <  convertVersionString(Extends.REQUIRED_PROTOTYPE))){
	   
			throw("Extends requires the Prototype JavaScript framework >= " + Extends.REQUIRED_PROTOTYPE);
		}
	
		$A(document.getElementsByTagName("script")).findAll(
			function(s) {
				return (s.src && s.src.match(/extends\.js(\?.*)?$/));
			}
		).each(
			function(s) {
				var path = s.src.replace(/extends\.js(\?.*)?$/,'');			
				var includes = s.src.match(/\?.*import=([a-z,]*)/);
				
				Extends.PATH = path;
				//chargement des bibliothèques statiques
				include(Extends.PATH + 'extends.color.js');
				include(Extends.PATH + 'extends.picture.js');
				include(Extends.PATH + 'extends.timer.js');
				include(Extends.PATH + 'extends.md5.js');
				
				//chargement des bibliothèques externe
				if(includes) includes[1].split(',').each(
				
					function(include) { 
						Extends.require(path+include+'.js');
					}
				);
	
				var lang = s.src.match(/\&.*lang=([a-z,]*)/);
				if(lang =='' || lang == null) MUI.setLang('fr');
				else MUI.setLang(lang[1]);
			});
		
		
	},
/**
 * Extends.observe(eventName, callback) -> undefined
 * - eventName (String): Nom de l'événement.
 * - callback (Function): Fonction lié au nom de l'événement.
 *
 * Enregistre une fonction lié à un nom d'événement dans le gestionnaire d'événement.
 * Cette fonction sera appellé lors du déclenchement de l'événement via `Extends.fire()`.
 * 
 **/
	observe: function(eventName, callback){
		if(!Object.isArray(this.callbacks[eventName])){
			this.callbacks[eventName] = [];	
		}
		this.callbacks[eventName].push(callback);
	},
/**
 * Extends.stopObserving(eventName, callback) -> undefined
 * - eventName (String): Nom de l'événement à stopper.
 * 
 * Stop un nom d'événement. Toutes les fonctions liées au nom ne seront plus appellés.
 *
 **/
	stopObserving: function(eventName, callback){
		if(Object.isUndefined(this.callbacks[eventName])){
			return;
		}
		var array = this.callbacks[eventName];
		this.callbacks[eventName] = new Array();
		
		for(var i = 0; i < array.length; i += 1){
			
			if(array[i] == callback) continue;
			this.callbacks[eventName].push(array[i]);
		}
	},
/** 
 * Extends.require(libraryName) -> undefined
 * - libraryName (String): Lien du script JS à inclure.
 * 
 * Inclut le fichier JS.
 * 
 **/
	require: function(libraryName) {
		// inserting via DOM fails in Safari 2.0, so brute force approach
		try{
			document.getElementsByName('head')[0].appendChild(new Node('script', {type:'text/javascript', src:libraryName}));	
		}catch(er){
			document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
		}
	},
/**
 * Extends.ready(callback) -> void
 * - callback (Function): Fonction lié au nom de l'événement.
 *
 * Cette méthode enregistre un écouteur et sera appellé lorsque la biliothèque JQuery, Extends et le Dom seront complétement chargé.
 * 
 **/
 	ready: function(callback){
		this.observe('dom:loaded', callback);
	},
/** 
 * Extends.include(libraryName) -> undefined
 * - libraryName (String): Lien du script JS à inclure.
 * 
 * Inclut le fichier JS.
 * 
 **/
	include: function(libraryName) {
		// inserting via DOM fails in Safari 2.0, so brute force approach
		try{
			document.getElementsByName('head')[0].appendChild(new Node('script', {type:'text/javascript', src:libraryName}));	
		}catch(er){
			document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
		}
	}
	
};
/** section: lang
 * include(libraryName) -> undefined
 * - libraryName (String): Lien du script JS à inclure.
 * 
 * Inclut le fichier JS.
 **/
function include(){
	var args = $A(arguments);
	
	if(args.length == 0) return;
	
	if(args.length == 1){
		Extends.include(args[0]);
	}else{
		for	(var i = 1; i < args.length; i+=1){
			
			Extends.include(args[0] + args[i] + '.js');
		}
	}
	return 
};
/** section: lang
 * Class
 * Manages Prototype's class-based OOP system. Refer to Prototype's web site for a tutorial on classes and inheritance.
 **/
 
/**
 * Class.createSprite(baliseName) -> Element Class
 * - baliseName (String): Nom de la balise. La classe héritera des propriétés de la balise demandée.
 *
 * Créer une classe hérité de la classe Element. Cette dernière pourra être ajouté à un `element` du DOM JavaScript.
 * 
 * ##### Exemple
 * 
 * Cet exemple montre comment utiliser la méthode [[Class.createSprite]].
 *
 *      var MaClasse = Class.createSprite('div');
 *            MaClasse.prototype = {
 *                  initialize: function(){
 *                  this.innerHTML = 'ma classe element';
 *            }
 *      };
 *      document.stage.body.appendChild(new MaClasse());
 * 
 * <p class="note">Tout comme les classes créer par `Class.create()`, vous pouvez écrire vos attributs classe dans le 
 * corps de MaClasse.prototype.</p>
 *
 **/
Class.createSprite = function() {
	var name = null, parent = null, properties = $A(arguments);
	
	if(Object.isString(properties[0])) {
		name = 			properties[0];
		properties = 	properties.shift();
	}
	if(Object.isFunction(properties[0])) parent = properties.shift();
	
	/**
	 * @ignore
	 */
	function klass() {
		
		if(name != null){
			var element  = new Node(name);

			Object.extend(element, this);

			element.initialize.apply(element, arguments);
						
			return element;
		}
		return this.initialize.apply(this, arguments);
	}
	
	Object.extend(klass, Class.Methods);
	klass.superclass = parent;
	klass.subclasses = [];
	
	if (parent) {
		/**
		 * @ignore
		 */
		var subclass = function() { };
		subclass.prototype = parent.prototype;
		klass.prototype = new subclass;
		parent.subclasses.push(klass);
	}
	
	for (var i = 0; i < properties.length; i++){
		try{
			//klass.addMethods(properties[i]);
		}catch(er){}
	}
	
	if(!klass.prototype.initialize)	klass.prototype.initialize = Prototype.emptyFunction;
	
	klass.prototype.constructor = klass;
	
	return klass;
};

/** section: lang
 * class Observer
 * La classe `Observer` est un gestionnaire d'événement personnalisé. Il vous permet de créer vos propres événements et ce
 * indépendement du gestionnaire d'événement du navigateur.
 *
 * #### Depuis la version 1.1
 *
 * * La classe Observer intègre un observeur d'événement par motif ou pattern, ce qui permet d'observer un groupe d'événement pour une seule fonction. 
 * * La méthode fire peut être stoppé à l'aide de l'objet [[StopEvent]].
 *
 **/
var Observer = Class.create();

Observer.prototype = {
/**
 * Observer.listerner -> Object Array
 * Liste des noms d'événements enregistrés dans le gestionnaire. 
 **/
	listener: null,
/**
 * Observer.patterns -> Object Array
 * Liste des patterns d'événements enregistrés dans le gestionnaire. 
 **/
	patterns: null,
/**
 * Observer.binding -> Mixed
 * Instance de l'objet utilisant lui même l'instance du gestionnaire courrant. (voir [[Observer.bind]]).
 **/
	binding: null,
/**
 * new Observer()
 * Créer une nouvelle instance du gestionnaire d'événement.
 **/
	initialize: function(){
		this.listener = {};
		this.patterns = {};
	},
/**
 * Observer.bind(instance) -> Observer
 * - instance (Mixed): Instance d'un objet utilisant le gestionnaire d'événement. 
 * 
 * Cette méthode ajoute une instance comme référence lors de l'utilisation de [[Observer.fire]]. (cf. `methode.apply(instanceMethode, args)`.
 * Elle évite l'emploi répétitif de la fonction `Function#bind()` dans une classe. Le contexte d'exécution de l'objet est 
 * automatiquement restitué, ce qui évite des erreurs de références lors de l'exécution du script JS.
 **/
	bind: function(instance){
		this.binding = instance;
		return this;
	},
/**
 * Observer.fire(eventName) -> Observer
 * - eventName (String): Nom de l'événement à déclencher.
 *
 * La méthode `fire` exécute un nom d'événement. L'ensemble des fonctions liées au nom d'événement seront appellées.
 *
 * ##### Exemple
 * 
 *      var calls = new Observer();
 *      calls.observe('myevent', function(){
 *          alert('hello world');
 *      };
 *      calls.fire('myevent'); //Affiche la boite de dialogue Hello world
 * 
 **/
	fire: function(eventName){
		
		var stopper = 		false;
		var properties = 	$A(arguments);
		var args = 			[];
	
		for(var i = 1; i < properties.length; i += 1){
			args.push(properties[i]);
		}
	
		try{
			if(args.length > 0){
				if(args[0].__class__ == 'stopevent'){
					args[0].eventName = eventName;
					stopper = args[0];
				}
			}
		}catch(er){}
		
		//lancement des patternListener
		for(var key in this.patterns){
			
			var reg = new RegExp(key);
			
			if(reg.test(eventName)){		
				for(var i = 0; i < this.patterns[key].length; i+=1){
					try{				
						if(Object.isFunction(this.patterns[key][i])){							
							this.patterns[key][i].apply(this.binding, args);
							
							if(stopper){
								//fin du parcours
								if(stopper.stopped) return this;
							}
						}
					}catch(er){
						try{$WT.trace(er);}catch(er){}	
					}
				}
			}
		}
			
		try{
			this.listener[eventName];
		}catch(er){return;}
			
		if(!Object.isUndefined(this.listener[eventName])){
			//lancement des listener
			for(var i = 0; i < this.listener[eventName].length; i+=1){
				try{				
					if(Object.isFunction(this.listener[eventName][i])){
						this.listener[eventName][i].apply(this.binding, args);
						
						if(stopper){
							//fin du parcours
							if(stopper.stopped) return this;
						}
					}
				}catch(er){
					try{$WT.trace(er);}catch(er){}	
				}
			}
			
		}
		return this;
	},
/**
 * Observer.observe(eventName, listener) -> Observer
 * - eventName (String): Nom de l'événement à observer.
 * - listener (Function): Fonction liée au nom de l'événement.
 * 
 * Observe un nom d'événement personnalisé et enregistre une fonction. Cette fonction sera appellé lors de l'utilisation
 * de la méthode [[Observer.fire]] avec comme paramètre le même nom d'événement.
 **/
	observe: function(eventName, callback){

		if(!Object.isArray(this.listener[eventName])){
			this.listener[eventName] = [];	
		}
		this.listener[eventName].push(callback);
		return this;
	},
/** alias of: Observer.observe
 * Observer.on(eventName, listener) -> Observer
 * - eventName (String): Nom de l'événement à observer.
 * - listener (Function): Fonction liée au nom de l'événement.
 * 
 * Observe un nom d'événement personnalisé et enregistre une fonction. Cette fonction sera appellé lors de l'utilisation
 * de la méthode [[Observer.fire]] avec comme paramètre le même nom d'événement.
 **/
	on: function(eventName, callback){
		return this.observe(eventName, callback);
	},
/**
 * Observer.observePattern(pattern, listener) -> Observer
 * - pattern (String): motif de recherche.
 * - listener (Function): Fonction liée au nom de l'événement.
 * 
 * Cette méthode observe tous les noms d'événements personnalisés ressemblant au motif `pattern`. La fonction enregistré sera appellé lors de l'utilisation
 * de la méthode [[Observer.fire]] avec comme paramètre le même nom d'événement proche du `pattern`.
 *
 * <p class="note">Cette méthode est disponible depuis la version 1.1</p>
 **/
	observePattern: function(pattern, callback){
		if(!Object.isArray(this.patterns[pattern])){
			this.patterns[pattern] = [];	
		}
		this.patterns[pattern].push(callback);
	},
/**
 * Observer.stopObserving(eventName) -> Observer
 * - eventName (String): Nom de l'événement à stopper
 *
 * Stop un événement enregistré dans le gestionnaire.
 **/
	stopObserving: function(eventName, callback){
		if(Object.isUndefined(this.listener[eventName])){
			return;
		}
		var array = this.callbacks[eventName];
		this.listener[eventName] = new Array();
		
		for(var i = 0; i < array.length; i += 1){
			
			if(array[i] == callback) continue;
			this.listener[eventName].push(array[i]);
		}
		return this;
	},
/** alias of: Observer.stopObserving
 * Observer.stop(eventName) -> Observer
 * - eventName (String): Nom de l'événement à stopper
 *
 * Stop un événement enregistré dans le gestionnaire.
 **/
 	stop: function(eventName){
		this.callbacks[eventName] = [];
		return this;
	}
};

/** section: lang
 * class StopEvent
 *
 * Stoppeur d'évenement généré à l'aide de la classe [[Observer]].
 * 
 * <p class="note">Cette classe est disponible depuis la version 1.1</p>
 **/
var StopEvent = Class.create();
StopEvent.prototype = {
	__class__: 'stopevent',
	eventName:	'',
	stopped: 	false,
	target:		null,
/**
 * new StopEvent([target])
 * - target (Mixin): Element cible de l'événement.
 *
 * Créer une nouvelle instance de `SysEvent`
 **/
	initialize: function(target){
		if(!Object.isUndefined(target)){
			this.target = target;
		}
	},
/**
 * StopEvent.stop() -> void
 *
 * Stop l'événement. La fonction appellant la méthode [[System.fire]] sera informé de l'arrêt de l'événement.
 **/
 	stop: function(){
		this.stopped = true;	
	}
};

/** section: DOM
 * class Node
 * Créer un nouvelle élément de type Node. Ce node peut être inseré à n'importe quel autre élément du DOM.
 **/
/**
 * new Node(tagName[, attributs[, nodes]])
 * - tagName (String): Nom de la balise à créer.
 * - attributs (Object): Par défaut, ce paramètre prend un objet d'attribut permettant de personnaliser la balise (par exemple `{style:'width:300px'}`). Vous pouvez passer en paramètre un tableau de Node à ajouter au corps de la balise plutot que les attributs (dans ce cas il ne faut pas passer ce tableau en troisième paramètre).
 * - nodes (Array): Tableau de Node à ajouter au corps de la balise.
 *
 * Créer une nouvelle instance de [[Node]].
 *
 * Il existe plusieurs façon d'instancier un [[Node]] comme ci-dessus :
 *
 * #### Avec un seul paramètre :
 *
 * * `new Node(tagName)`.
 *
 * #### Avec deux paramètres :
 *
 * * `new Node(tagName, attributs)` où est un `Object` contenu les attributs d'un `Element`.
 * * `new Node(tagName, [Node1, Node2, ..., Noden])`, de cette façon vous ajoutez des Nodes au corps du Node sans ajouter d'atributs au Node courant.
 * * `new Node(tagName, String)`, de cette façon vous créez un [[Node]] avec un texte quelconque dans son corps.
 *
 * #### Avec les trois paramètres :
 *
 * * `new Node(tabName, attributs, nodes)`, de cette façon le [[Node]] est initialisé avec des attributs et des Nodes ajoutés à son corps.
 * * `new Node(tabName, attributs, string)`, de cette façon le [[Node]] est initialisé avec des attributs et un texte quelconque.
 *
 **/
var Node = function(tagName, obj, append){

	var element = new Element(tagName);

	//arg[1]
	if(!Object.isUndefined(obj)){

		if(Object.isElement(obj)) element.appendChild(obj);
		else{
			if(Object.isString(obj) || Object.isNumber(obj)){
				var text = document.createTextNode(obj);
				element.appendChild(text);
			}

			else{
				if((typeof obj) == "object" && !Object.isArray(obj)){
					//element.writeAttribute(obj);
					for(var key in obj){
						if(key == "style") element.setStyle(obj[key]);
						else {
							try{
								element[key] = obj[key];	
							}catch(er){
								element.writeAttribute({key:obj[key]});
							}
						}
					}

				}
				if(Object.isArray(obj)){

					for(var i = 0; i < obj.length; i++){
						if(Object.isElement(obj[i])){
							element.appendChild(obj[i]);
							continue;
						}
						if(Object.isString(obj[i]) || Object.isNumber(obj[i])){
							var text = document.createTextNode(obj[i]);
							element.appendChild(text);
						}
						
					}	
				}
			}
		}
	}
	
	//arg[2]
	if(!Object.isUndefined(append)){
		if(Object.isString(append) || Object.isNumber(append)){

			var text = document.createTextNode(append);
			element.appendChild(text);
		}
		if(Object.isElement(append)){

			element.appendChild(append);
		}
		if(Object.isArray(append)){

			for(var i = 0; i < append.length; i++){
				
				if(Object.isElement(append[i])){
					element.appendChild(append[i]);
					continue;
				}
				if(Object.isString(append[i]) || Object.isNumber(append[i])){
					var text = document.createTextNode(append[i]);

					element.appendChild(text);
				}
			}	
		}
	}
	
	return element;
};
/** section: DOM
 *  class Element
 *
 *  The [[Element]] object provides a variety of powerful DOM methods for
 *  interacting with DOM elements&nbsp;&mdash; creating them, updating them,
 *  traversing them, etc. You can access these either as methods of [[Element]]
 *  itself, passing in the element to work with as the first argument, or as
 *  methods on extended element *instances*:
 *
 *      // Using Element:
 *      Element.addClassName('target', 'highlighted');
 *
 *      // Using an extended element instance:
 *      $('target').addClassName('highlighted');
 *
 *  Element is also a constructor for building element instances from scratch,
 *  see [`new Element`](#new-constructor) for details.
 *
 *  Most [[Element]] methods return the element instance, so that you can chain
 *  them easily:
 *
 *      $('message').addClassName('read').update('I read this message!');
 *
 *  ##### More Information
 *
 *  For more information about extended elements, check out ["How Prototype
 *  extends the DOM"](http://prototypejs.org/learn/extensions), which will walk
 *  you through the inner workings of Prototype's DOM extension mechanism.
 **/

/**
 *  new Element(tagName[, attributes])
 *  - tagName (String): The name of the HTML element to create.
 *  - attributes (Object): An optional group of attribute/value pairs to set on
 *    the element.
 *
 *  Creates an HTML element with `tagName` as the tag name, optionally with the
 *  given attributes. This can be markedly more concise than working directly
 *  with the DOM methods, and takes advantage of Prototype's workarounds for
 *  various browser issues with certain attributes:
 *
 *  ##### Example
 *
 *      // The old way:
 *      var a = document.createElement('a');
 *      a.setAttribute('class', 'foo');
 *      a.setAttribute('href', '/foo.html');
 *      a.appendChild(document.createTextNode("Next page"));
 *
 *      // The new way:
 *      var a = new Element('a', {'class': 'foo', href: '/foo.html'}).update("Next page");
**/
Node.Methods = {
/**
 * Element.appendChild(@element, node) -> Node
 * - node (Node | Element): Element à ajouter au corps du node.
 *
 * Ajoute un [[Node]] au [[Node]] courant.
 **/

/**
 * Element.addChildAt(@element, node, it) -> Node
 * - node (Node | Element): Element à ajouter au corps de la balise.
 * - it (Number): Indice à laquelle il faut ajouter la balise.
 *
 *  Ajoute un [[Node]] à un l'indice demandé. Cette méthode permet d'inserer un [[Node]] entres deux enfants du [[Node]] parent.
 **/
	addChildAt:function(node, child, nb){

		if(Object.isUndefined(nb)){
			node.a(child);
		}
		if(nb == 0) return node.top(child);
		
		var childs = node.childElements();
		node.appendChild(child);

		for(var i = nb; i < childs.length; i +=1){
			node.removeChild(childs[i]);
			node.appendChild(childs[i]);
		}
		
		return node;
	},
/**
 * Element.appendChilds(@element, nodeArray) -> Node
 * - nodeArray (Node): Tableau de [[Node]] à ajouter au corps du [[Node]] courant.
 *
 * Ajoute un ou plusieurs [[Node]] au [[Node]] courant.
 **/
	appendChilds:function(node, array){
		
		for(var i = 0; i < array.length; i += 1){
			if(Object.isElement(array[i])){
				node.a(array[i]);
				continue;
			}
			if(Object.isString(array[i]) || Object.isNumber(array[i])){
				var text = document.createTextNode(array[i]);
				node.a(text);
			}
		}
		return node;
	},
/** alias of: Element.appendChild
 * Element.appendChilds(@element, nodeArray) -> Node
 * - node (Node | Element): Element à ajouter au corps du node.
 *
 * Ajoute un [[Node]] au [[Node]] courant.
 **/
	a: function(node, child){return node.appendChild(child);},
/** alias of: Element.addChildAt
 * Element.at(@element, node, it) -> Node
 * - node (Node | Element): Element à ajouter au corps du node.
 * - it (Number): Indice à laquelle il faut ajouter l'element.
 *
 *  Ajoute un [[Node]] à un l'indice demandé. Cette méthode permet d'inserer un [[Node]] entres deux enfants du [[Node]] parent.
 **/
	at: function(node, child, i){return node.addChildAt(node, child, it);},
/** alias of: Element.appendChilds
 * Element.as(@element, nodeArray) -> Node
 * - nodeArray (Node): Tableau de [[Node]] à ajouter au corps du [[Node]] courant.
 *
 * Ajoute un ou plusieurs [[Node]] au [[Node]] courant.
 **/
	as: function(node, array){return node.appendChilds(node, array);},
 
/** alias of: Event.observe
 * Element.click(@element, listener) -> Node
 * - listener (Function): Fonction appellé lors du `click` sur l'élément.
 *
 * Cette méthode est un raccourci [[Event.observe]] et est spécialisé pour le click.
 **/
	click: function(node, callback){
		return node.on('click', callback);
	},
/** alias of: Event.observe
 * Element.on(@element, listener) -> Node
 * - listener (Function): Fonction appellé sur le nom de l'événement.
 *
 * Cette méthode est un raccourci [[Event.observe]].
 **/
	on: function(node){var args = $A(arguments); args.shift(); return node.observe.apply(node, args);},
/** alias of: Event.observe
 * Element.mouseover(@element, listener) -> Node
 * - listener (Function): Fonction appellé lors du `mouseover` sur l'élément.
 *
 * Cette méthode est un raccourci [[Event.observe]] et est spécialisé pour le `mouseover`.
 **/
 	mouseover: function(node, callback){
		return node.on('mouseover', callback);
	},
/** alias of: Event.observe
 * Element.mouseout(@element, listener) -> Node
 * - listener (Function): Fonction appellé lors du `mouseout` sur l'élément.
 *
 * Cette méthode est un raccourci [[Event.observe]] et est spécialisé pour le `mouseout`.
 **/
 	mouseout: function(node, callback){
		return node.on('mouseout', callback);
	},
/** alias of: Event.observe
 * Element.mousedown(@element, listener) -> Node
 * - listener (Function): Fonction appellé lors du `mousedown` sur l'élément.
 *
 * Cette méthode est un raccourci [[Event.observe]] et est spécialisé pour le `mousedown`.
 **/
	mousedown: function(node, callback){
		return node.on('mousedown', callback);
	},
/** alias of: Event.observe
 * Element.mouseup(@element, listener) -> Node
 * - listener (Function): Fonction appellé lors du `mouseup` sur l'élément.
 *
 * Cette méthode est un raccourci [[Event.observe]] et est spécialisé pour le `mouseup`.
 **/
	mouseup: function(node, callback){
		return node.on('mouseup', callback);
	},
/** alias of: Event.observe
 * Element.mousemove(@element, listener) -> Node
 * - listener (Function): Fonction appellé lors du `mousemove` sur l'élément.
 *
 * Cette méthode est un raccourci [[Event.observe]] et est spécialisé pour le `mouseup`.
 **/
	mousemove: function(node, callback){
		return node.on('mousemove', callback);
	},
/** alias of: Event.observe
 * Element.keyupcode(@element, listener, code) -> Node
 * - listener (Function): Fonction appellé lorsque la touche associé au code du second paramètre sera relaché.
 * - code (Number): Code de la touche clavier à intercepter.
 *
 * Cette méthode est un raccourci [[Event.observe]]. La fonction enregistrée sera appellée lorsque la touche `code`
 * sera relaché. Cette méthode fonctionne avec tous les éléments du DOM et plus particulièrement avec les balises
 * de type saisie (INPUT, TEXTEAREA, etc...).
 **/
	keyupcode: function(node,code, callback){
		return node.on('keyup', function(evt){
			if(Event.getKeyCode(evt) == code){
				return callback.call(this, evt);
			}
		});
	},
/** alias of: Event.observe
 * Element.keydowncode(@element, listener, code) -> Node
 * - listener (Function): Fonction appellé lorsque la touche associé au code du second paramètre sera pressé.
 * - code (Number): Code de la touche clavier à intercepter.
 *
 * Cette méthode est un raccourci [[Event.observe]]. La fonction enregistrée sera appellée lorsque la touche `code`
 * sera pressé. Cette méthode fonctionne avec tous les éléments du DOM et plus particulièrement avec les balises
 * de type saisie (INPUT, TEXTEAREA, etc...).
 **/
	keydowncode: function(node, code, callback){
		return node.on('keydown', function(evt){
			if(Event.getKeyCode(evt) == code){
				return callback.call(this, evt);
			}
		});
	},
/** alias of: Element.keyupcode
 * Element.keyupenter(@element, listener) -> Node
 * - listener (Function): Fonction appellé lorsque la touche ENTRER est relaché sur l'élément.
 *
 * Cette méthode est un raccourci [[Element.keyupcode]], sauf que vous n'avez pas besoin de mentionner le `code` clavier de la touche `ENTRER`. 
 * La fonction enregistré sera appellé lorsque la touche `ENTRER` sera relaché. Cette méthode fonctionne avec tous les éléments du DOM et plus 
 * particulièrement avec les balises de type saisie (INPUT, TEXTEAREA, etc...).
 **/
	keyupenter: function(node, callback){
		return node.on('keyup', function(evt){
			if(Event.getKeyCode(evt) == Event.KEY_RETURN){
				return callback.call(this, evt);
			}
		});
	},
/** alias of: Element.keydowncode
 * Element.keydownenter(@element, listener) -> Node
 * - listener (Function): Fonction appellé lorsque la touche ENTRER est pressé sur l'élément.
 *
 * Cette méthode est un raccourci [[Element.keydowncode]], sauf que vous n'avez pas besoin de mentionner le `code` clavier de la touche `ENTRER`. 
 * La fonction enregistré sera appellé lorsque la touche ENTRER sera pressé. Cette méthode fonctionne avec tous les éléments du DOM et plus 
 * particulièrement avec les balises de type saisie (INPUT, TEXTEAREA, etc...).
 **/
	keydownenter: function(node,callback){
		return node.on('keydown', function(evt){
			if(Event.getKeyCode(evt) == Event.KEY_RETURN){
				return callback.call(this, evt);
			}
		});
	},
/**
 * Element.top(@element, child) -> Node
 * - child (Node): Node à ajouter.
 * 
 * Ajoute un enfant au debut en tête des enfants de l'element courant
 *
 **/
	top: function(node, child){
		node.insert({top:child});
		return node;
	},
	//ignore
	topChild: function(node, child){
		node.insert({top:child});
		return node;
	},
	//ignore
	getChildAt: function(node, id){
		var childs = node.childElements();
		try{
			var child = childs[id];
		}catch(er){
			child = false;	
		}
		return child;
	},
/**
 * Element.removeChilds(@element[, array]) -> Node
 * - array (Array): Liste de Node à supprimer du DOM.
 * 
 * Supprime tous les enfants de l'élement. Si `array` est passé alors seulement
 * la liste des elements sera supprimé.
 *
 **/
	removeChilds: function (node, array){
		if(Object.isArray(array)) var childs = array;
		else var childs = node.childElements();
		
		if(!(document.navigator.client == 'IE' && document.navigator.version < 8)){
			for(var i = 0; i < childs.length; i +=1){
				try{node.removeChild(childs[i]);}catch(er){}
			}
		}
		node.innerHTML = '';
		return node;
	},
	//ignore
	rm: function(node){return node.removeChilds(node);},
/**
 * Element.constraint(@element, x[, y[, xd[, yd]]]) -> Node
 * - x (Number): Coordonnée de la 1ère droite horizontale selon l'axe des X (0 par défaut, pas de contrainte).
 * - y (Number): Coordonnée de la 1ère droite verticale selon l'axe des Y (0 par défaut, pas de contrainte).
 * - xd (Number): Coordonnée de la 2e droite horizontale selon l'axe des X (0 par défaut, pas de contrainte).
 * - yd (Number): Coordonnée de la 2e droite verticale selon l'axe des Y (0 par défaut, pas de contrainte).
 *
 * Cette méthode défini un rectangle correspondant à zone de drag, c'est-à-dire de déplacement possible lors du mouvement du [[Node]].
 * Par défaut la zone n'a pas de limite et l'element peut être déplacé sur toute la surface de la page.
 * 
 * Vous pouvez définir jusqu'à 4 droites, 2 par axe selon X et 2 axe selon Y afin de définir une zone de déplacement.
 * Chacun des axes sont défini par rapport au point le plus haut du document HTML (au coin en haut à gauche).
 * 
 * ##### Exemples
 * 
 * On désire créer une contrainte lors du déplacement du [[Node]] sur la scène :
 *
 *     var node = new Node('div', {style: 'width:20px; height:20px; border:solid 1px black'});
 *     node.constraint(50); //je contraint le mouvement selon le premier axe
 *
 *     //ajout du node au DOM
 *     $Body.appendChild(node);
 *
 *     //création du drag
 *     node.createDrag();
 *    
 * De la même façon vous pouvez ajouter les droites de contraintes comme suivant :
 *
 *     node.constraint(50,50); //contrainte selon les deux premiers axes à 50 pixel de chaque bord.
 *
 *     node.constraint(50,50, 250); //contrainte selon les trois premiers axes à 50 pixel de chaque bord pour les deux premiers et 250 pour le dernier.
 *
 *     node.constraint(50,50, 250, 250); //contrainte selon les 4 axes, à 50 pixel de chaque bord pour les deux premiers et 250 pour les derniers.
 *
 * Ou ne pas contraindre certaines droites :
 *     
 *     node.constraint(0,0, 300);
 *
 * <p class="note">Les valeurs de <code>xd</code> et <code>yd</code> doivent être supérieur aux valeurs de <code>x</code> et <code>y</code> (sauf si c'est 0). Le fait de choisir
 * des valeurs inférieurs ne génère pas d'erreur, en revanche le mouvement sera impossible vue que la zone est incompréhensible.</p>
 *
 **/
	constraint: function(node, x1,y1, x2, y2){
		node.constX1 = x1;
		node.constY1 = y1;
		node.constX2 = x2;
		node.constY2 = y2;
		return node;
	}, 
/**
 * Element.createDrag(@element[, nodeclick]) -> Node
 * - nodeclick (Element | Node | Function): (optionnel) Element à cliquer pour déclencher le drag'n'drop.
 *
 * Cette méthode permet d'activer le Drag'n'Drop sur l'élément courant. 
 *
 * Par défaut le paramètre `nodeclick` de la méthode [[Element.createDrag]] n'est pas nécessaire et prend pour valeur l'`element` courant. Dans ce cas, ce sera le `mousedown` sur l'`element` qui  activera le déplacement du conteneur.
 * 
 * ##### Passage d'un Node en second paramètre
 * 
 * Si vous passez le second paramètre un [[Node]] ou [[Element]] (de préférence un [[Node]] enfant de l'élément en courant), ce dernier deviendra la zone cliquable du Drag'n'Drop.
 * Cette méthode est utile lorsque vous créez des éléments de type fenêtre (Boite de dialogue par exemple, où l'entete est la zone cliquable).
 *
 * ##### Passe d'un Object en second paramètre
 *
 * Si vous passez en second paramètre un `Object` vous pourrez configurer le drag'n'drop de façon plus complete. L'objet prend différent attributs comme décrit ci-dessus :
 * 
 * * `constX1`, `constX2`, `constY1`, `constY2` : Applique une contrainte sur le mouvement du Drag'n'Drop. (Voir la méthode `Element.constrainte`).
 * * `onDrag` : Enregistre une fonction qui sera appellé lors du déplacement du  [[Node]]
 * * `onEndDrag` : Enregistre une fonction qui sera appellé après drag'n'drop
 * * `current` : Définit le [[Node]] cliquable pour le Drag'n'drop
 *
 * ##### Evenements liés au drag'n'drop
 *  
 * Le drag'n'drop déclenche trois événements :
 *
 * * `drag:start` : Déclenché au début du déplacement du drag'n'drop.
 * * `drag:snap` : Déclenché pendant le déplacement du `node`.
 * * `drag:end` : Déclenché à la fin du drag'n'drop.
 *
 * ##### Exemples
 *
 * Dans cet exemple, nous allons créer un [[Node]] simple pour le drag'n'drop :
 *  
 *     var node = new Node('div', {style: 'width:20px; height:20px; border:solid 1px black'});
 *    
 *     //ajout du node au DOM
 *     $Body.appendChild(node);
 *
 *     //création du drag
 *     node.createDrag();
 *
 * Dans ce second exemple, nous allons créer un [[Node]] ayant des enfants. L'un des enfants sera la zone cliquable et activera le déplacement du [[Node]] parent :
 *
 *     var node = new Node('div', {style: 'width:300px; height:300px; border:solid 1px black'});
 *     var header = new Node('div', {style: 'height:'20px; border-bottom:1px solid black'}); 
 *     
 *     node.appendChild(header);
 *     //ajout du node au DOM
 *     $Body.appendChild(node);
 *
 *     //création du drag
 *     node.createDrag(header);
 *
 **/
	createDrag: function(node, elem){
		
		node.backup = 		{};
		node.constX1 = 		0;
		node.constX2 = 		0;
		node.constY1 = 		0;
		node.constY2 = 		0;
		node.onDrag =		'';
		node.onEndDrag =	'';
		node.droppable =  	[];
		node.current =		null;
		
		if(!Object.isElement(node.current)){
			try{
				node.current.stopObserving('mousedown', this.startdrag_bind);
				node.startdrag_bind = 	null;
				node.current = 		null;
			}catch(er){}
		}

		if(Object.isElement(elem)) node.current = elem;	
		else{
			if(Object.isObject()){
				
				for(var key in elem){
					Object.extend(node, elem);
				}
					
			}else{
				node.curent = node;
			}
			
		}

		node.startdrag_bind = node.startDrag.bindAsEventListener(node);
		node.current.observe('mousedown', node.startdrag_bind);

		return node;
	},
	//ignore
	onSnap: function(node, evt){
		if(!node.isMove){
			node.stopDrag(evt);
			return;
		}
		
		try{
			var Coord = {};
	
			Coord.x = Event.pointerX(evt) - node.deltaX_;
			Coord.y = Event.pointerY(evt) - node.deltaY_;
			
			if((node.constX1 != node.constX2) || (node.constY1 != node.constY2)){
				
				if(Coord.x  < node.constX1) 					Coord.x = node.constX1;
				if(Coord.x + node.getWidth() >= node.constX2) 	Coord.x = node.constX2 - node.getWidth() +1;
				
				if(Coord.y < node.constY1) 						Coord.y = node.constY1;
				if(Coord.y + node.getHeight() >= node.constY2)  Coord.y = node.constY2 - node.getHeight()+1;
			}
			
			if(Object.isFunction(node.onDrag)){
				Coord = node.onDrag.call(node, evt, Coord); 
				if(!Coord) return;
			}
				
			node.setStyle({top: Coord.y+"px", left: Coord.x + "px"});
			
			node.fire('drag:snap');
		}
		catch(er){};
	},
/**
 * Element.removeDrag(@element) -> Node
 *
 * Cette méthode supprime les événements liés au Drag'n'Drop, ajouté lors de l'utilisation de la méthode [[Element.createDrag]].
 * L'élément ne sera plus draggable jusqu'à la prochaine utilisation de la méthode [[Element.createDrag]]
 *
 **/
	removeDrag: function(node){
		try{
			node.current.stopObserving('mousedown', node.startdrag_bind);
			node.startdrag_bind = null;
			node.current = null;
		}catch(er){}
		return node;
	},
	/*
	 * Active le drag'n'drop sur l'element courant
	 * @returns {Node}
	 */
	startDrag: function(node, evt){

		if(!Object.isElement(node.current))	node.createDrag();
		if(Object.isUndefined(evt)) return 	false;
		evt.stop();
		
		node.backup.top = node.positionedOffset().top;
		node.backup.left = node.positionedOffset().left;
		node.backup.zIndex = node.getStyle('zIndex');

		node.deltaX_ = Event.pointerX(evt) - node.backup.left;
		node.deltaY_ = Event.pointerY(evt) - node.backup.top;

		node.setOpacity(0.7);
		node.setStyle({zIndex:10000});
		
		if(node.isMove) return;
		
		node.onsnap_bind = 		node.onSnap.bindAsEventListener(node);
		node.stopdrag_bind = 	node.stopDrag.bindAsEventListener(node);
		
		document.observe('mousemove', node.onsnap_bind);	
		document.observe('mouseup', node.stopdrag_bind);
		
		node.isMove = true;
		
		node.fire('drag:start');
		
		return node;
	},
	/*
	 * Stop le drap'n'drop du Node
	 */
	stopDrag: function(node, evt){
		if(!node.isMove) return;

		node.isMove = false;

		try{
			document.stopObserving('mousemove', node.onsnap_bind);	
			document.stopObserving('mouseup', node.stopdrag_bind);
			
		}catch(er){}
		
		try{
			node.stopdrag_bind = '';
			node.onsnap_bind = '';
					
			node.setOpacity(1);
		}catch(er){}
		
		try{
			node.setStyle({"zIndex":node.backup.zIndex=='null'?0:node.backup.zIndex});
		}catch(er){}
		
		if(Object.isFunction(node.onEndDrag)) node.onEndDrag.call(node, evt, node);
		node.fire('drag:end');
		
		return node;
	},
/**
 * Element.revert(@element) -> Node
 *
 * Repositionne l'élément à sa position précèdente.
 **/
	revert: function(node){
		try{
			node.MoveTo(node.backup.left, node.backup.top);
		}catch(er){
				
		}
		return node;
	},
/**
 * Element.createMask(@element, mask[, type]) -> Node
 * - mask (String): suite de caractère # pour la création d'un mask. # permet d'indiquer à la méthode que ce caractère pourra être remplacé par un caractère de saisie.
 * - type (String): Indique si la saisie n'autorise que les entiers (type => number).
 *
 * Créer un masque de saisie pour un champs de saisie.
 * Cette méthode permet de formater l'affichage du champs et contraindre la saisie de différent
 * type comme celui d'une date, d'un entier, d'un numéro de téléphone, etc...
 *
 * ##### Exemples
 *
 * Dans cette exemple nous allons créer un champ de saisie et nous allons y ajouter un masque pour le
 * téléphone :
 *
 *      var node = new Node('input', {type:'text'});
 *      node.createMask('## ## ## ## ##', 'number'); //le masque est spécifique au téléphone de 10 numéros.
 *
 **/
	createMask: function(node, mask, type){
		
		if(type == 'number'){
			node.readOnly = 'readonly';
		}
		
		node.observe('keydown', function(evt){
			if(evt.keyCode == 8 && type == 'number'){
				node.value_ = node.value_.slice(0, node.value_.length - 1);
				
				if(Object.isUndefined(node.value_)){
					node.value_ = '';
				}
			}
		});
		try{
		if(Object.isUndefined(type)){
			node.value_ = node.value;	
		}
		}catch(er){}

		node.observe('keyup', function(evt){

			var num = false, pad = false;
			
			if(Object.isUndefined(node.value_)) {
				node.value_ = node.value.unformat(mask);
			}

			if(((num = $R(48, 57).include(evt.keyCode)) || (pad = $R(96, 105).include(evt.keyCode))) && type == 'number'){
				
				if(num) {
					node.value_ += evt.keyCode - 48;
				}
				if(pad) {
					node.value_ += evt.keyCode - 96;
				}	
				
			}
			
			if(node.value_.length > 0){
				
				var str = 		'';

				for(var i = 0, y = 0; y < node.value_.length && i < mask.length; i+=1){

					var char = mask[i];
					
					if(char == "#"){
						str += node.value_[y];
						y++;
						
					}else{
						str += mask[i];
					}
				}
				
				node.value = str;
				node.value_ = node.value_.slice(0, y);
				
			}else{
				node.value = '';	
			}
			
		});
		return node;
	},
	/*
	 * Effet de transition. Passe de l'opacité 0 à 1 progressivement.
	 * @param {Integer} obj Configuration de configuration de la translation
	 *<ul>
	 *		<li>{@link Number} delay retard de la translation</li>
	 *		<li>{@link Number} duration : temps de la translation</li>
	 *		<li>{@link Number} mode : relative ou absolute</li>
	 * 		<li>{@link Number} onComplete : fonction appellé après la transition</li>
	 *</ul>
	 */
	Appear: function(node, obj){
		
		node.setOpacity(0);
		
		var options = {
			duration: 	Extends.NODE_DURATION,
			delay:		0,
			finish:		0,
			count:		0,
			time:		0,
			frame:		0,
			fps:		Extends.NODE_FPS
		};
		
		if(!Object.isUndefined(obj)){
			Object.extend(options, obj);
		}
		
		options.finish = 	options.duration + options.delay;
		options.frame =		options.fps * options.duration;
		options.opacity = 	1 / options.frame;
		
		options.on = function(n){
			n.setOpacity(this.opacity * this.count);
		};
		
		options.end = function(n){n.setOpacity(1)};
		
		var onEffect = function(){
			Node.Methods.onEffect(node, options);	
		};
		
		options.timer = new Timer(onEffect.bind(node), 1 / options.fps);
		options.timer.start();
		
		onEffect = null;
				
		return node;
	},
	/*
	 * Effet de transition. Passe de l'opacité 1 à 0 progressivement.
	 * @param {Integer} obj Configuration de configuration de la translation
	 *<ul>
	 *		<li>{@link Number} delay retard de la translation</li>
	 *		<li>{@link Number} duration : temps de la translation</li>
	 *		<li>{@link Number} mode : relative ou absolute</li>
	 * 		<li>{@link Number} onComplete : fonction appellé après la transition</li>
	 *</ul>
	 */
	Disappear: function(node, obj){

		node.setOpacity(1);
		
		var options = {
			duration: 	Extends.NODE_DURATION,
			delay:		0,
			finish:		0,
			count:		0,
			time:		0,
			frame:		0,
			fps:		Extends.NODE_FPS
		};
		
		if(!Object.isUndefined(obj)){
			Object.extend(options, obj);
		}
		
		options.finish = 	options.duration + options.delay;
		options.frame =		options.fps * options.duration;
		options.opacity = 	1 / options.frame;
		
		options.on = function(n){
			n.setOpacity(1 - (this.opacity * this.count));
		};
		
		options.end = function(n){n.setOpacity(0)};
		
		var onEffect = function(){
			Node.Methods.onEffect(node, options);	
		};
		
		options.timer = new Timer(onEffect.bind(node), 1 / options.fps);
		options.timer.start();
		
		onEffect = null;
		
		return node;
	},
	/*
	 * Effet de transition. L'objet apparait par agrandissement.
	 * @param {Integer} obj Configuration de configuration de la translation
	 *<ul>
	 *		<li>{@link Number} delay retard de la translation</li>
	 *		<li>{@link Number} duration : temps de la translation</li>
	 *		<li>{@link String} direction : type d'agrandissement (center, top-left, top-right, bottom-left, bottom-right)</li>
	 * 		<li>{@link Number} onComplete : fonction appellé après la transition</li>
	 *</ul>
	 */
	Grow: function(node, obj){
		
		var options = {
			duration: 	Extends.NODE_DURATION,
			delay:		0,
			finish:		0,
			count:		0,
			time:		0,
			frame:		0,
			fps:		Extends.NODE_FPS,
			direction:	'center',
			dimension:	{
				width: 	1 * node.getStyle('width').replace('px', ''),
				height:	1 * node.getStyle('height').replace('px', '')
			},
			dim:		{width:0, height:0}
		};
		
		if(!Object.isUndefined(obj)){
			Object.extend(options, obj);
		}

		options.coord =	{y:parseFloat(node.getStyle('top') || '0'), x:parseFloat(node.getStyle('left') || '0')};
		
		options.finish = 	options.duration + options.delay;
		options.frame =		options.fps * options.duration;

		options.move = {
			width:	options.dimension.width / options.frame,
			height:	options.dimension.height / options.frame
		};
		
		//modification du style
		var childs = node.childElements();
		
		for(var i = 0; i < childs.length; i++){
			childs[i].setStyle('visibility:hidden');
		};
		node.setOpacity(1);
		node.setStyle('width:0px;height:0px');
		
		switch(options.direction) {
			case 'top-left':
			  	break;
			case 'top-right':
				node.setStyle({
					left: 	(options.coord.x + options.dimension.width) + 'px'
				});
				node.MoveTo(options.coord.x, options.coord.y, {duration:options.duration});
			 	break;
			case 'bottom-left':
				node.setStyle({
					top:	(options.coord.y + options.dimension.height) + 'px'
				});
				node.MoveTo(options.coord.x, options.coord.y, {duration:options.duration});
				break;
			case 'bottom-right':
				node.setStyle({
					left: 	(options.coord.x + options.dimension.width) + 'px',
					top:	(options.coord.y + options.dimension.height) + 'px'
				});
				node.MoveTo(options.coord.x, options.coord.y, {duration:options.duration});	
				break;
			case 'center':
				node.setStyle({
					left: 	(options.coord.x + (options.dimension.width / 2)) + 'px',
					top:	(options.coord.y + (options.dimension.height / 2)) + 'px'
				});
				node.MoveTo(options.coord.x, options.coord.y, {duration:options.duration});				
				break;
		}
		
		
		
		options.on = function(n){
			this.dim.height += 	this.move.height;
			this.dim.width += 	this.move.width;
			
			node.setStyle({
				width: 	this.dim.width + 'px',
				height:	this.dim.height + 'px'
			});
		};
		
		options.end = function(n){
			node.setStyle({
				width: 	this.dimension.width + 'px',
				height:	this.dimension.height + 'px'
			});
			
			var childs = n.childElements();
			
			for(var i = 0; i < childs.length; i++){
				childs[i].setStyle('visibility:visible');
				childs[i].Appear({duration:0.1});
			};
		};
		
		var onEffect = function(){
			Node.Methods.onEffect(node, options);	
		};
		
		options.timer = new Timer(onEffect.bind(node), 1 / options.fps);
		options.timer.start();
		
		onEffect = null;
		
		return node;
	},
	/*
	 * Effet de transition. L'objet disparait par miniaturisation.
	 * @param {Integer} obj Configuration de configuration de la translation
	 *<ul>
	 *		<li>{@link Number} delay retard de la translation</li>
	 *		<li>{@link Number} duration : temps de la translation</li>
	 *		<li>{@link String} direction : type d'agrandissement (center, top-left, top-right, bottom-left, bottom-right)</li>
	 * 		<li>{@link Number} onComplete : fonction appellé après la transition</li>
	 *</ul>
	 */
	Shrink: function(node, obj){
		
		var options = {
			duration: 	Extends.NODE_DURATION,
			delay:		0,
			finish:		0,
			count:		0,
			time:		0,
			frame:		0,
			fps:		Extends.NODE_FPS,
			direction:	'center',
			dimension:	{
				width: 	1 * node.getStyle('width').replace('px', ''),
				height:	1 * node.getStyle('height').replace('px', '')
			},
			dim:		{width:0, height:0}
		};
		
		if(!Object.isUndefined(obj)){
			Object.extend(options, obj);
		}
		
		options.dim =	{width:options.dimension.width, height:options.dimension.height};
		
		options.coord =	{y:parseFloat(node.getStyle('top') || '0'), x:parseFloat(node.getStyle('left') || '0')};
		
		options.finish = 	options.duration + options.delay;
		options.frame =		options.fps * options.duration;

		options.move = {
			width:	options.dimension.width / options.frame,
			height:	options.dimension.height / options.frame
		};
		
		//modification du style
		var childs = node.childElements();
		
		for(var i = 0; i < childs.length; i++){
			childs[i].setStyle('visibility:hidden');
		};
				
		switch(options.direction) {
			case 'none':
			case 'top-left':
			  	break;
			case 'top-right':
				node.MoveTo(options.coord.x + options.dimension.width, options.coord.y, {duration:options.duration});
			 	break;
			case 'bottom-left':
				node.MoveTo(options.coord.x, options.coord.y + options.dimension.height, {duration:options.duration});
				break;
			case 'bottom-right':
				node.MoveTo(options.coord.x + options.dimension.width, options.coord.y + options.dimension.height, {duration:options.duration});	
				break;
			case 'center':

				node.MoveTo(options.coord.x + (options.dimension.width / 2), 
							options.coord.y + (options.dimension.height / 2), {duration:options.duration});				
				break;
		}
		
		node.Disappear({duration:options.duration});
		
		options.on = function(n){
			this.dim.height -= 	this.move.height;
			this.dim.width -= 	this.move.width;
			
			node.setStyle({
				width: 	this.dim.width + 'px',
				height:	this.dim.height + 'px'
			});
		};
		
		options.end = function(n){
			node.setStyle({
				width: 	this.dimension.width + 'px',
				height:	this.dimension.height + 'px'
			});
			
			node.setOpacity(0);
			
			var childs = n.childElements();
			
			for(var i = 0; i < childs.length; i++){
				childs[i].setStyle('visibility:visible');
				childs[i].setOpacity(1);
			};
		};
		
		var onEffect = function(){
			Node.Methods.onEffect(node, options);	
		};
		
		options.timer = new Timer(onEffect.bind(node), 1 / options.fps);
		options.timer.start();
		
		onEffect = null;
		
		return node;
	},
	/*
	 * Effect de translation simple et progressive.
	 * @param {Integer} x Coordonnées en pixel
	 * @param {Integer} y Coordonnées en pixel
	 * @param {Integer} obj Configuration de configuration de la translation
	 *<ul>
	 *		<li>{@link Number} delay retard de la translation</li>
	 *		<li>{@link Number} duration : temps de la translation</li>
	 *		<li>{@link Number} mode : relative ou absolute</li>
	 * 		<li>{@link Number} onComplete : fonction appellé après la transition</li>
	 *</ul>
	 */
	MoveTo:function(node, x, y, obj){
		
		var options = {
			duration: 	Extends.NODE_DURATION,
			delay:		0,
			finish:		0,
			count:		0,
			time:		0,
			frame:		0,
			fps:		Extends.NODE_FPS,
			x:			x,
			y:			y
		};
		
		if(!Object.isUndefined(obj)){
			Object.extend(options, obj);
		}
		
		options.coord =	{y:parseFloat(node.getStyle('top') || '0'), x:parseFloat(node.getStyle('left') || '0')};
		
		//calcul du mouvement
		options.finish = 	options.duration + options.delay;
		options.frame =		options.fps * options.duration;
		options.move = {
			x:	(x - options.coord.x) / options.frame,
			y:	(y - options.coord.y) / options.frame
		};
		
		options.on = function(n){
			
			this.coord.x += this.move.x;
			this.coord.y += this.move.y;
			
			n.setStyle({
				left: 	Math.round(this.coord.x) + 'px',
				top:	Math.round(this.coord.y) + 'px'
			});
		};
		
		var onEffect = function(){
			Node.Methods.onEffect(node, options);	
		};
		
		options.timer = new Timer(onEffect.bind(node), 1 / options.fps);
		options.timer.start();
		
		onEffect = null;
		
		return node;
	},
	/*
	 * @private
	 * @ignore
	 */
	onEffect: function(node, options){
		
		options.time += 1 / options.fps;
		
		if(options.time < options.delay) return;
		
		if(options.count < options.frame){
			options.on(node);
			options.count++;
		}else{
				
			options.timer.stop();
			options.timer = null;
			options.on = null;
			
			if(!Object.isUndefined(options.end)) {
				options.end(node);
				options.end = null;
			}
			
			if(Object.isFunction(options.onComplete)) {
				options.onComplete.call(node, node);
				options.onComplete = null;
			}
		}
	},
	/*
	 * Assigne une fonction sur l'evenement ondrag
	 * @param {Function} fn Ecouteur de l'evenement ondrag
	 * @returns {Node}
	 */
	setOnDrag:function(node, fn){
		if(!Object.isUndefined(fn)) node.onDrag	= fn;
		return node;
	},
	/*
	 * Assigne une fonction sur l'evenement enddrag
	 * @param {Function} fn Ecouteur de l'evenement enddrag
	 * @returns {Node}
	 */
	setOnEndDrag: function(node, fn){
		if(!Object.isUndefined(fn)) node.onEndDrag	= fn;	
		return node;
	}
};

Element.addMethods(Node.Methods);

if(typeof Builder == "undefined"){//support de l'ancienne bibliothèque Builder.node
	var Builder = {
		node:function(tagName, obj, append){
			return new Node(tagName, obj, append);	
		}
	};
}
/** section: lang
 * MUI
 * Ce module met à disposition un outil simple de traduction de langue. Ce module ce base sur un fichier de traduction et
 * un tableau associatif de mots qui seront remplacé par leurs traductions équivalentes.
 **/
var MUI = {
/**
 * MUI.lang -> String
 * Indique la langue actuellement chargé par le traducteur.
 **/
	lang:'',
/**
 * MUI.month -> Object
 * Contient un tableau associatif des langues. Pour chaque langue on associe un tableau de mois qui serviront 
 * à la traduction.
 **/
	month:	{
/*
 * MUI.month.fr -> Array
 * Description des mois dans la langue française.
 **/
		fr:	['Janvier', 'Fevrier', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Aout', 'Septembre', 'Octobre', 'Novembre', 'Decembre'],
/*
 * MUI.month.eng -> Array
 * Description des mois dans la langue anglaise.
 **/
		eng:['January', 'February', 'Marsh', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
	},
/**
 * MUI.days -> Object
 * Contient un tableau associatif des langues. Pour chaque langue on associe un tableau de jour qui serviront 
 * à la traduction.
 **/
	days: 	{
/*
 * MUI.days.fr -> Array
 * Description des jours dans la langue française.
 **/
		fr:	['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
/*
 * MUI.days.eng -> Array
 * Description des jours dans la langue anglaise.
 **/
		eng:['Sunday','Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
	},
/**
 * MUI.text -> Object
 * Contient un tableau associatif des langues. L'objet text contient une liste de mot écrit dans l'application
 * cible et associe leur traduction.
 **/
	text:	{fr:{}, eng:{}},
	err:	{fr:{}, eng:{}},
/**
 * MUI.addWord(key, word, lang) -> void
 * - key (String): Mot clef de l'application cible.
 * - word (String): Traduction du mot clef dans la langue cible.
 * - lang (String): Langue associée à la traduction.
 *
 * Cette méthode ajoute une traduction d'un mot au dictionnaire de mot.
 **/
	addWord:function(key, wordtrad, lang){
		if(Object.isUndefined(lang)) lang = this.lang;
		this.text[lang][key.toLowerCase()] = wordtrad;
	},
/**
 * MUI.addWords(obj, lang) -> void
 * - obj (Object): Dictionnaire de mot à ajouter.
 * - lang (String): Langue associée à la traduction.
 *
 * Cette méthode ajoute un dictionnaire de mot au dictionnaire courrant.
 *
 * ##### Description du paramètre obj
 *
 * Le paramètre obj doit être décrit de la façon suivante :
 *	
 *     var obj = {
 *         'mot cible': 'mot traduit',
 *         'mot cible2': 'mot traduit 2',
 *         //etc...
 *     }
 *
 *
 **/
	addWords: function(obj, lang){
		for(var key in obj){
			this.addWord(key, obj[key], lang);	
		}
	},
	//ignore
	addError:function(key, wordtrad, lang){
		if(Object.isUndefined(lang)) lang = this.lang;
		this.err[lang][key.toLowerCase()] = wordtrad;
	},
	//ignore
	addErrors: function(obj, lang){
		for(var key in obj){
			this.addError(key, obj[key], lang);	
		}
	},
/**
 * MUI.translate(word) -> String
 * - word (String): Mot à traduire.
 *
 * Traduit le mot à partir de langue par défaut et de son dictionnaire associé.
 * 
 * ##### Exemple
 *
 * Voici un exemple simple d'utilisation :
 * 
 *     MUI.addWord('bonjour le monde', 'hello world', 'eng');
 *     
 *     MUI.lang = 'eng';
 *     alert(MUI.translate('bonjour le monde')); //hello world
 *     //autre fonction plus rapide à écrire
 *     alert($MUI('bonjour le monde')); //hello world
 *
 **/
	translate: function(text){
		if(this.lang == '') return text;
		var value = this.text[this.lang][text];

		if(value =='' || Object.isUndefined(value))	{
			value = this.text[this.lang][text.toLowerCase()];
			if(value =='' || Object.isUndefined(value)) return text;
		}
		return value;
	},
	//ignore
	translateErr: function(text){
		if(this.lang == '') return text;
		
		var value = this.err[this.lang][text];
	
		if(value =='' || Object.isUndefined(value))	return text;
		return value;
	},
/**
 * MUI.getMonth(it) -> String
 * - it (Number): Numéro du mois.
 * 
 * Cette méthode prend en paramètre le numéro du mois entre 0 et 11, et retourne la traduction
 * du mois en fonction de la langue. Si la traduction du mois n'existe pas dans la langue
 * choisie, la méthode retournera la traduction de langue par défaut (Anglais).
 **/
	getMonth: function(i){
		var value ='';
		try{
			value = this.month[this.lang][i];
		}
		catch(er){
			value = this.month['eng'][i];
		}
		return value;
	},
	//ignore
	getMonths: function(i){
		return this.month[this.lang].clone();
	},
/**
 * MUI.getDay(it) -> String
 * - it (Number): Numéro du jour.
 * 
 * Cette méthode prend en paramètre le numéro du jours entre 0 et 6, et retourne sa traduction
 * en fonction de la langue. Si la traduction du jour n'existe pas dans la langue
 * choisie, la méthode retournera la traduction de langue par défaut (Anglais).
 **/
	getDay: function(i){

		var value ='';
		try{
			value = this.days[this.lang][i];
		}
		catch(er){
			try{
				value = this.days['eng'][i];
			}catch(er){
				return i;	
			}
		}
		return value;
	},
/**
 * MUI.setLang(lang) -> void
 * - lang (lang): Langue du dictionnaire.
 *
 * Cette méthode change la langue par défaut du dictionnaire de mot et charge le
 * fichier de traduction associé à la langue dans le dossier de référence
 * (/MUI/LANG_lang.js). 
 *
 * Si lang est fr alors le fichier chargé sera FR_fr.js. En théorie le fichier
 * multilingue doit contenir l'ensemble des mots de traduction de l'application
 * cible.
 *
 **/
	setLang: function(lang){
		this.lang = lang.toLowerCase();
		Extends.require(Extends.URL + 'MUI/'+this.lang.toUpperCase()+'_'+this.lang.toLowerCase()+'.js');
	}
};
/** alias of: MUI.translate, section: lang
 * $MUI(text) -> String
 * 
 * Cette fonction permet la traduction de mot vers une langue cible.
 * Cette langue cible est définit par la classe [[MUI]].
 *
 * ##### Exemple
 *
 * Voici un exemple simple d'utilisation :
 * 
 *     MUI.addWord('bonjour le monde', 'hello world', 'eng');
 *     MUI.lang = 'eng';
 *     alert(MUI.translate('bonjour le monde')); //hello world
 *     //autre fonction plus rapide à écrire
 *     alert($MUI('bonjour le monde')); //hello world
 **/
function $MUI(text){
	return MUI.translate(text);
}
//ignore
function $ERR(text){
	return MUI.translateErr(text);
}
/*
 * $_GET
 * Tableau des paramètres GET du script en cours d'execution.
 * var get = $_GET['op'];
 **/
var $_GET = [];

/** section: lang
 * class String
 *
 * L'objet `String` permet de manipuler les chaines de caractère facilement en javascript.
 * La bibliothèque Extends ajoute un certain nombre de méthode à la classe `String`
 * en plus de celle déjà ajouté par Prototype.
 * 
 * #### Qu'en est-il de ces méthodes ? 
 * 
 * La majorité des méthodes décrite par Extends sont tirés du langage PHP
 * dans leur version Objet, afin de faciliter la transition pour le développeur entre PHP et Javascript.
 *
 *
 **/
Object.extend(String.prototype, (function(){
/**
 * String#toDate() -> Date
 *
 * Analyse la chaine de caractère et tente une conversion de type String vers Date.
 *
 * ##### Exemple 
 *
 * Dans cette exemple nous allons convertir une chaine de caractère de date au format français vers
 * sont équivalent Date :
 *
 *     var madate = "10/12/2010";
 *     alert(typeof madata); //String
 *     madate = madate.toDate(); 
 *     alert(typeof madata); //object Date
 *
 * <p class="note">Cette méthode ne génère pas d'erreur si la chaine de caractère est invalide</p>
 **/
	function toDate(){
		var date = new Date();
		var string = this.split(' ');
		
		if(string.length >1){
			var h = string[1].split(':');
			date.setHours(h[0], h[1] || 0, h[2] || 0);
			
		}
	
		string = string[0];
	
		if(string.substr(4,1) == "-") {
			
			string = string.split('-');
			
	
			date.setFullYear(string[0]);
			date.setMonth(new Number(string[1]) -1, string[2]);
		}
		else {
		
			string = string.split('/');

			date.setFullYear(string[2]);
			date.setMonth(new Number(string[1]) -1, string[0]);
		}
	
		return date;	
	}
/**
 * String#htmlEntities() -> String
 *
 * Convertie tous les caractères éligibles en entités HTML.
 **/
	function htmlEntities(){
		/*
		 * @ignore
		 */
		function convert(char){

			var htmlentities = {' ':'nbsp','¡':'iexcl','¢':'cent','£':'pound','¤':'curren','¥':'yen','¦':'brvbar','§':'sect','¨':'uml','©':'copy','ª':'ordf','«':'laquo','¬':'not','­':'shy','®':'reg','¯':'macr','°':'deg','±':'plusmn','²':'sup2','³':'sup3','´':'acute','µ':'micro','¶':'para','·':'middot','¸':'cedil','¹':'sup1','º':'ordm','»':'raquo','¼':'frac14','½':'frac12','¾':'frac34','¿':'iquest','×':'times','÷':'divide','ƒ':'fnof','•':'bull','…':'hellip','′':'prime','″':'Prime','‾':'oline','⁄':'frasl','℘':'weierp','ℑ':'image','ℜ':'real','™':'trade','ℵ':'alefsym','←':'larr','↑':'uarr','→':'rarr','↓':'darr','↔':'harr','↵':'crarr','⇐':'lArr','⇑':'uArr','⇒':'rArr','⇓':'dArr','⇔':'hArr','∀':'forall','∂':'part','∃':'exist','∅':'empty','∇':'nabla','∈':'isin','∉':'notin','∋':'ni','∏':'prod','∑':'sum','−':'minus','∗':'lowast','√':'radic','∝':'prop','∞':'infin','∠':'ang','∧':'and','∨':'or','∩':'cap','∪':'cup','∫':'int','∴':'there4','∼':'sim','≅':'cong','≈':'asymp','≠':'ne','≡':'equiv','≤':'le','≥':'ge','⊂':'sub','⊃':'sup','⊄':'nsub','⊆':'sube','⊇':'supe','⊕':'oplus','⊗':'otimes','⊥':'perp','⋅':'sdot','\u2308':'lceil','\u2309':'rceil','\u230a':'lfloor','\u230b':'rfloor','\u2329':'lang','\u232a':'rang','◊':'loz','♠':'spades','♣':'clubs','♥':'hearts','♦':'diams','"':'quot','>':'gt','ˆ':'circ','˜':'tilde',' ':'ensp',' ':'emsp',' ':'thinsp','‌':'zwnj','‍':'zwj','‎':'lrm','‏':'rlm','–':'ndash','—':'mdash','‘':'lsquo','’':'rsquo','‚':'sbquo','“':'ldquo','”':'rdquo','„':'bdquo','†':'dagger','‡':'Dagger','‰':'permil','‹':'lsaquo','›':'rsaquo','€':'euro',' ':'nbsp','¡':'iexcl','¢':'cent','£':'pound','¤':'curren','¥':'yen','¦':'brvbar','§':'sect','¨':'uml','©':'copy','ª':'ordf','«':'laquo','¬':'not','­':'shy','®':'reg','¯':'macr','°':'deg','±':'plusmn','²':'sup2','³':'sup3','´':'acute','µ':'micro','¶':'para','·':'middot','¸':'cedil','¹':'sup1','º':'ordm','»':'raquo','¼':'frac14','½':'frac12','¾':'frac34','¿':'iquest','×':'times','÷':'divide','ƒ':'fnof','•':'bull','…':'hellip','′':'prime','″':'Prime','‾':'oline','⁄':'frasl','℘':'weierp','ℑ':'image','ℜ':'real','™':'trade','ℵ':'alefsym','←':'larr','↑':'uarr','→':'rarr','↓':'darr','↔':'harr','↵':'crarr','⇐':'lArr','⇑':'uArr','⇒':'rArr','⇓':'dArr','⇔':'hArr','∀':'forall','∂':'part','∃':'exist','∅':'empty','∇':'nabla','∈':'isin','∉':'notin','∋':'ni','∏':'prod','∑':'sum','−':'minus','∗':'lowast','√':'radic','∝':'prop','∞':'infin','∠':'ang','∧':'and','∨':'or','∩':'cap','∪':'cup','∫':'int','∴':'there4','∼':'sim','≅':'cong','≈':'asymp','≠':'ne','≡':'equiv','≤':'le','≥':'ge','⊂':'sub','⊃':'sup','⊄':'nsub','⊆':'sube','⊇':'supe','⊕':'oplus','⊗':'otimes','⊥':'perp','⋅':'sdot','\u2308':'lceil','\u2309':'rceil','\u230a':'lfloor','\u230b':'rfloor','\u2329':'lang','\u232a':'rang','◊':'loz','♠':'spades','♣':'clubs','♥':'hearts','♦':'diams','"':'quot','>':'gt','ˆ':'circ','˜':'tilde',' ':'ensp',' ':'emsp',' ':'thinsp','‌':'zwnj','‍':'zwj','‎':'lrm','‏':'rlm','–':'ndash','—':'mdash','‘':'lsquo','’':'rsquo','‚':'sbquo','“':'ldquo','”':'rdquo','„':'bdquo','†':'dagger','‡':'Dagger','‰':'permil','‹':'lsaquo','›':'rsaquo','€':'euro', 'À':'Agrave','Á':'Aacute','Â':'Acirc','Ã':'Atilde','Ä':'Auml','Å':'Aring','Æ':'AElig','Ç':'Ccedil','È':'Egrave','É':'Eacute','Ê':'Ecirc','Ë':'Euml','Ì':'Igrave','Í':'Iacute','Î':'Icirc','Ï':'Iuml','Ð':'ETH','Ñ':'Ntilde','Ò':'Ograve','Ó':'Oacute','Ô':'Ocirc','Õ':'Otilde','Ö':'Ouml','Ø':'Oslash','Ù':'Ugrave','Ú':'Uacute','Û':'Ucirc','Ü':'Uuml','Ý':'Yacute','Þ':'THORN','ß':'szlig','à':'agrave','á':'aacute','â':'acirc','ã':'atilde','ä':'auml','å':'aring','æ':'aelig','ç':'ccedil','è':'egrave','é':'eacute','ê':'ecirc','ë':'euml','ì':'igrave','í':'iacute','î':'icirc','ï':'iuml','ð':'eth','ñ':'ntilde','ò':'ograve','ó':'oacute','ô':'ocirc','õ':'otilde','ö':'ouml','ø':'oslash','ù':'ugrave','ú':'uacute','û':'ucirc','ü':'uuml','ý':'yacute','þ':'thorn','ÿ':'yuml','Œ':'OElig','œ':'oelig','Š':'Scaron','š':'scaron','Ÿ':'Yuml', 'Α':'Alpha','Β':'Beta','Γ':'Gamma','Δ':'Delta','Ε':'Epsilon','Ζ':'Zeta','Η':'Eta','Θ':'Theta','Ι':'Iota','Κ':'Kappa','Λ':'Lambda','Μ':'Mu','Ν':'Nu','Ξ':'Xi','Ο':'Omicron','Π':'Pi','Ρ':'Rho','Σ':'Sigma','Τ':'Tau','Υ':'Upsilon','Φ':'Phi','Χ':'Chi','Ψ':'Psi','Ω':'Omega','α':'alpha','β':'beta','γ':'gamma','δ':'delta','ε':'epsilon','ζ':'zeta','η':'eta','θ':'theta','ι':'iota','κ':'kappa','λ':'lambda','μ':'mu','ν':'nu','ξ':'xi','ο':'omicron','π':'pi','ρ':'rho','ς':'sigmaf','σ':'sigma','τ':'tau','υ':'upsilon','φ':'phi','χ':'chi','ψ':'psi','ω':'omega','\u03d1':'thetasym','\u03d2':'upsih','\u03d6':'piv'};
			
			if(!Object.isUndefined(htmlentities[char])) return "&"+htmlentities[char]+";";
			return char;
		}
		var tmp = '';
		for(var i = 0; i < this.length; i++){
			tmp += convert(this.substr(i,1));
		}
		return tmp;
	}
/**
 * String#html_entity_decode(quotestyle) -> String
 * - quotestyle (String): Le paramètre optionnel quote_style vous permet de définir ce qu'il adviendra des guillemets simples et doubles. 
 *
 * `html_entity_decode()` est la fonction contraire de [[String#htmlEntities]] : elle convertit les entités HTML de la chaîne string en caractères normaux. 
 *
 * #### Conversion prise en charge :
 *
 * Le paramètre `quotestyle` peut prendre les valeurs suivantes :
 *
 * * `HTML_SPECIALCHARS`.
 * * `HTML_ENTITIES`.
 * * `ENT_NOQUOTES` : Ne convertit aucun guillemet.
 * * `ENT_COMPAT` : Convertit les guillemets doubles et ignore les guillemets simples.
 * * `ENT_QUOTES` : Convertit les guillemets doubles et les guillemets simples.
 *
 *
 * #### Rendons à César ce qui est à César !
 *
 * L'auteur original de cette méthode John, <a href="http://www.jd-tech.net">http://www.jd-tech.net</a>. 
 * Donc merci à John pour cette superbe méthode.
 *
 **/
	function html_entity_decode(quote_style) {
		var string = this;
		//if(quote_style == undefined) quote_style = 'HTML_SPECIALCHARS';
 
		var hash_map = {}, symbol = '', tmp_str = '', entity = '';
		tmp_str = string.toString();
		
		if (false === (hash_map = document.get_html_translation_table('HTML_ENTITIES', quote_style))) {
			return false;
		}
	 
		for (symbol in hash_map) {
			entity = hash_map[symbol];
			tmp_str = tmp_str.split(entity).join(symbol);
		}
		tmp_str = tmp_str.split('&#039;').join("'");
		
		return tmp_str;
	}
/**
 * String#isMail() -> Boolean
 *
 * Analyse et vérifie la syntaxe de l'e-mail. Si il y a correspondance, la méthode retoune true.
 **/
	function isMail(){
		var reg = new RegExp("([@]([a-z0-9]+)[\-]?([a-z0-9]+)[\.](([a-z]+)[\.]?([a-z]+)))");
		return reg.exec(this);
	}
/**
 * String#isDate() -> Boolean
 *
 * Analyse et vérifie la syntaxe de la date au format String. Si il y a correspondance, la méthode retoune true.
 **/
	function isDate(){
		var regdate = new RegExp("([0-9]{2})/([0-9]{2})/([0-9]{4})");
		var tab;
		var tMonth;
		if(regdate.exec(this)) {
			tab = this.split("/");
			tMonth = (new Date(tab[2], 0, 1)).getArrayDaysByMonth();
			if(tab[1] > 12) return false;
			if(tab[0] < 0 && tab[0] > tMonth[tab[1] - 1]) return false;
			return true;
		}
		regdate = new RegExp("([0-9]{4})-([0-9]{2})-([0-9]{2})");
		if(regdate.exec(this)){
			tab = this.split("-");
			tMonth = (new Date(tab[0], 0, 1)).getArrayDaysByMonth();
			if(tab[1] > 12) return false;
			if(tab[2] < 0 && tab[2] > tMonth[tab[1] - 1]) return false;
			return true;
		}
		return false;
	}
/**
 * String#isTel() -> Boolean
 *
 * Analyse et vérifie la syntaxe du numéro de téléphone. Si il y a correspondance, la méthode retoune true.
 * Les numéros pris en charge sont les numéros français, allant du 01 au 09 en préfixe.
 **/
	function isTel(){
		var regtel = new RegExp("^(01|02|03|04|05|06|07|08|09)[0-9]{8}");
		return regtel.test(this);
	}
/**
 * String#isNumber() -> Boolean
 *
 * Analyse et vérifie la syntaxe du nombre au format String. Si il y a correspondance, la méthode retoune true.
 * Les numéros pris en charge sont les numéros français, allant du 01 au 09 en préfixe.
 **/
	function isNumber(){
		return !isNaN(this);
	}
/**
 * String#toNumber() -> Number
 *
 * Cette methode tente la conversion du type [[String]] vers le type [[Number]].
 **/
	function toNumber(){
		if(this.isNumber) return 1 * this;
		return false;
	}
/**
 * String#isCodePostal() -> Boolean
 *
 * Analyse et vérifie la syntaxe du code postal. Si il y a correspondance, la méthode retoune true.
 **/
	function isCodePostal(){
		var sp = this.substr(0,2);
		
		var reg = new RegExp("^((0[1-9])|([1-8][0-9])|(9[0-8])|(2A)|(2B))[0-9]{3}$", 'g');
		
		return reg.test(this);
	}
/**
 * String#isSiret() -> Boolean
 *
 * Analyse et vérifie la syntaxe du numero de siret. Si il y a correspondance, la méthode retoune true.
 **/
	function isSiret(){

		var somme = 0;
		var tmp;
		//temporairement
		return true;
		if ((this.length != 14) || (isNaN(this)) ) return false;

		 // Donc le SIRET est un numérique à 14 chiffres
		 // Les 9 premiers chiffres sont ceux du SIREN (ou RCS), les 4 suivants
		 // correspondent au numéro d'établissement
		 // et enfin le dernier chiffre est une clef de LUHN.
		 
		 for (var cpt = 0; cpt < this.length; cpt++) {
			if ((cpt % 2) == 0) { // Les positions impaires : 1er, 3è, 5è, etc...
					tmp = this.charAt(cpt) * 2; // On le multiplie par 2
					if (tmp > 9) tmp -= 9; // Si le résultat est supérieur à 9, on lui soustrait 9
			}
			else tmp = this.charAt(cpt);
			somme += parseInt(tmp);
		 }
		 
		 if ((somme % 10) == 0) return true; // Si la somme est un multiple de 10 alors le SIRET est valide
		 return false;
	}
/**
 * String#isSiren() -> Boolean
 *
 * Analyse et vérifie la syntaxe du numero de siren. Si il y a correspondance, la méthode retoune true.
 **/
	function isSiren(){

		var somme = 0;
		var tmp;
		if((this.length != 9) || (isNaN(this)))return false;

		// Donc le SIREN est un numérique à 9 chiffres
		for (var cpt = 0; cpt < this.length; cpt++){
			if((cpt % 2) == 1){ // Les positions paires : 2ème, 4ème, 6ème et 8ème chiffre
				tmp = this.charAt(cpt) * 2; // On le multiplie par 2
				if (tmp > 9) tmp -= 9; // Si le résultat est supérieur à 9, on lui soustrait 9
			}
			else tmp = this.charAt(cpt);
			somme += parseInt(tmp);
		}
		if ((somme % 10) == 0) return true; // Si la somme est un multiple de 10 alors le SIREN est valide
		return false;

	}	
	/*
	 * Déspécialise les caractères spéciaux du string courant
	 * @returns {String}
	 */
/**
 * String#addslashes() -> String
 *
 * Retourne la chaîne str, après avoir échappé tous les caractères qui doivent l'être, pour être utilisée dans une requête de base de données. 
 * Ces caractères sont les guillemets simples * ('), guillemets doubles ("), antislash (\) et NUL (le caractère NULL).
 *
 * Un exemple d'utilisation d'addslashes() est lorsque vous entrez des données dans une base de données. Par exemple, pour insérer le nom O'reilly dans la base, 
 * vous aurez besoin de le protéger. 
 * 
 * ##### Exemple
 * 
 *    var str = "Votre nom est-il O'reilly ?";
 *    // Affiche : Votre nom est-il O\'reilly ?
 *    alert(str.addslashes());
 *
 **/
	function addslashes(){
		return this.replace(/\\("|'|\\)/g, "$1");
	}
/**
 * String#stripslashes() -> String
 *
 * Supprime les antislashs d'une chaîne.
 * 
 * ##### Exemple
 * 
 *    var str = "Votre nom est-il O\'reilly ?";
 *    // Affiche : Votre nom est-il O'reilly ?
 *    alert(str.addslashes());
 *
 **/
	function stripslashes(){
		 return this.replace(/("|'|\\)/g, "\\$1");
	}	
/**
 * String#format(mask) -> String
 * - mask (String): Masque pour le formatage.
 *
 * Formate la chaine de caractère en fonction d'un masque de saisie.
 * Cette méthode est similaire à [[Element.createMask]].
 * 
 * ##### Exemple
 * 
 *     var str = "0164214200";
 *     alert(str.format('## ## ## ## ##'); //01 64 21 42 00
 *
 **/
	function format(mask){
						
		var str = 		'';

		for(var i = 0, y = 0; y < this.length && i < mask.length; i += 1){
			
			var char = mask.slice(i,i+1);
		
			if(char == "#"){
				str += this.slice(y,y+1);
				y++;
			}else{
				str += char;
			}
		}
		
		return str;
	}
/**
 * String#unformat(mask) -> String
 * - mask (String): Masque pour le formatage.
 *
 * Cette méthode est l'inverse de [[String#format]]. Elle restitue une chaine non formaté.
 * 
 * ##### Exemple
 * 
 *    var str = "01 64 21 42 00";
 *    alert(str.unformat('## ## ## ## ##'); //0164214200
 *
 **/
	function unformat(mask){
		var str = 		'';

		for(var i = 0; i < mask.length && i < this.length; i += 1){
			
			var char = mask[i];
		
			if(char == "#"){
				str += this[i];
			}
		}
		
		return str;
	}
/** 
 * String#insert(intIndex, strChar) -> String
 * - intIndex (Number): Position d'index de l'insertion.
 * - strChar (String): Chaine à inserer.
 *
 * Insère une instance spécifiée de String au point d'index indiqué dans cette instance.
 **/
	function insert(intIndex, strChar){
 		if(isNaN(intIndex))	return this;
 		if(intIndex < 0)	return this;
 
 		if(!strChar) return this;
		strChar += '';
 		intIndex = parseInt(intIndex, 10);
 		return (this.substr(0, intIndex) + strChar + this.substr(intIndex, this.length));
	}
/**
 * String#padLeft(totalWidth, paddingChar) -> String
 * - totalWidth (Number): Nombre de caractères dans la chaîne qui en résulte, égal au nombre de caractères d'origine plus tout caractère de remplissage supplémentaire. 
 * - paddingChar (String): Caractère Unicode de remplissage. 
 *
 * Aligne les caractères de cette instance à droite et, à gauche, remplit en ajoutant des espaces ou un caractère Unicode spécifié pour une longueur totale spécifiée.
 **/
	function padLeft(){
 		if(arguments.length == 0 || (arguments.length >= 1 && isNaN(arguments[0])) || arguments[0] < 0) return this;
 
 		var strThis = this, intLength = parseInt(arguments[0], 10), strChar = String.fromCharCode(32);
 		
		if(arguments.length == 2) 	strChar = '' + arguments[1];
 		while (strThis.length < intLength) strThis = strChar + strThis;
		
 		return strThis;
	}
/**
 * String#padRight(totalWidth, paddingChar) -> String
 * - totalWidth (Number): Nombre de caractères dans la chaîne qui en résulte, égal au nombre de caractères d'origine plus tout caractère de remplissage supplémentaire. 
 * - paddingChar (String): Caractère Unicode de remplissage. 
 *
 * Aligne les caractères de cette chaîne à gauche et remplit à droite en ajoutant un caractère Unicode spécifié pour une longueur totale spécifiée.
 **/
	function padRight(){
 		if(arguments.length == 0 || (arguments.length >= 1 && isNaN(arguments[0])) || arguments[0] < 0) return this;
	
		var strThis = this, intLength = parseInt(arguments[0], 10), strChar = String.fromCharCode(32);
 		if(arguments.length == 2) strChar = '' + arguments[1];
		
 		while (strThis.length < intLength) strThis = strThis + strChar;
 		return strThis;
	}
/**
 * String#remove(intIndex, intLength) -> String
 * - intIndex(Number): Supprime de la chaîne tous les caractères en commençant à la position spécifiée et en continuant jusqu'à la dernière position.
 * - intLength(Number): Supprime un nombre spécifié de caractères de cette instance en commençant à une position définie.
 *
 * Retourne une nouvelle chaîne dans laquelle un nombre spécifié de caractères de cette instance est supprimé.
 **/
	function remove(intIndex, intLength){
 		if(isNaN(intIndex) || isNaN(intLength) || intIndex < 0 || intLength < 0) return this;

 		intIndex = parseInt(intIndex, 10);
		intLength = parseInt(intLength, 10);
 		return (this.substr(0, intIndex) + this.substr(intIndex + intLength, this.length));
	}
/**
 * String#trim() -> String
 *
 * Supprime toutes les occurrences d'un jeu de caractères spécifié à partir du début et de la fin de cette instance. 
 **/
	function trim(){
 		return this._replace(/(^\s*)|(\s*$)/g, '');
	}
/**
 * String#rtrim() -> String
 *
 * Supprime toutes les occurrences d'un jeu de caractères spécifié à partir du début et de la fin de cette instance. 
 **/
	function rtrim(){
 		return this._replace(/\s*$/g, '');
	}
/**
 * String#ltrim() -> String
 *
 * Supprime toutes les occurrences d'un jeu de caractères spécifié à partir du début et de la fin de cette instance. 
 **/
	function ltrim(){
 		return this._replace(/^\s*/g, '');
	}
/**
 * String#sanitize(charReplace) -> String
 * - charReplace (String): Caractère de remplacement des caractères spéciaux n'ayant pas d'équivalent.
 *
 * Supprime tous les caractères accentués de la chaine et les remplaces par leurs équivalent non accentués.
 **/
	function sanitize(charRemplacement){
		
		if(Object.isUndefined(charRemplacement)) charRemplacement = "-";
		
		var string = this.replace(/ /g, charRemplacement);
		string = encodeURIComponent(string);
		string = string.replace(/(%C3%A9)|(%C3%A8)|(%C3%AA)|(%C3%AB)/g,'e');
		string = string.replace(/(%C3%A0)|(%C3%A4)|(%C3%A2)/g,'a');
		string = string.replace(/(%C3%B9)|(%C3%BC)|(%C3%BB)/g,'u');
		string = string.replace(/(%C3%BF)/g,'y');
		string = string.replace(/(%C3%B2)|(%C3%B6)|(%C3%B4)/g,'o');
		string = string.replace(/(%C3%A7)/g,'c');
		string = string.replace(/%[A-F0-9]{0,2}/g, '');
		
		var reg = new RegExp('/\\\\|\\/|\\||\\:|\\?|\\*|"|<|>|[[:cntrl:]]/','g');
		string = string.replace(reg, charRemplacement);
		string = string.replace(/[~'!]/g, '');
		
		return string;
	}
/**
 * String#toMoney() -> String
 *
 * Transforme la chaine au format Money pour l'affichage de feuille de Calcul.
 **/
	function toMoney(){
		var prix = this.split('.');
		if(prix[0] == '' || prix[0] == '0') prix[0] = '0';
		else{
			var prix_ = '';
			var nb = prix[0].length -1;
			
			for(var i = nb; i >= 0; i -= 1) {				
				if((nb - i) % 3 == 2) prix_ = " " + prix[0][i] + prix_;
				else prix_ = prix[0][i] + prix_;
			}
			
			prix[0] = prix_;
		}
		if(Object.isUndefined(prix[1])) prix[1] = '00';
		else{
			prix[1] = Math.round(prix[1] * 100) / 100;	
		}

		if(prix.length == 2) return prix[0] + "," + (prix[1]+"0").slice(0, 2);
		return prix[0] + ",00";
	}
/** alias of: $MUI
 * String#mui() -> String
 **/
	function mui(){
		return $MUI(this);
	}
/**
 * String#isHexa() -> Boolean
 *
 * Analyse et vérifie la syntaxe de la chaine Hexadecimal. Si il y a correspondance, la méthode retoune true.
 **/
	function isHexa(){
		var regDec = /^[a-fA-F\d]+$/;
		if (!regDec.test(this)) return false;
		return true;
	}
/**
 * String#toDecimal() -> Number
 *
 * Cette méthode tente de convertire une chaine hexadecimal en nombre décimal.
 **/
	function toDecimal(){
		var oHex = this;
		// Vérif condition mini ok
		if (!this.isHexa()) return false;
		var result = parseInt(oHex,16);
		return result;
	}
/**
 * String#toHexa() -> String
 *
 * Cette méthode tente de convertire une chaine décimal en héxadecimal.
 **/
	function toHexa(){
		var oDec = this;
		if(oDec == undefined) return false;
		
		var regDec = /^\d+$/;
	
		if (!regDec.test(oDec)) return false;
		
		return parseInt(oDec,10).toString(16).toUpperCase();
	}
/**
 * String#utf8_decode() -> String
 * 
 * Cette méthode convertie une chaine encodé en UTF-8 vers une chaine encodé en ISO-8859-1.
 *
 * Auteur de la méthode Webtoolkit.info (<a href="http://www.webtoolkit.info/">http://www.webtoolkit.info/</a>)
 **/
	function utf8_decode () {

		str_data = this;
		str_data += '';
		var i = 0, ac = 0, c1 = 0, c2 = 0, c3 = 0;
		var tmp_arr = [];
		
		while ( i < str_data.length ) {        c1 = str_data.charCodeAt(i);
			if (c1 < 128) {
				tmp_arr[ac++] = String.fromCharCode(c1);
				i++;
			} else if ((c1 > 191) && (c1 < 224)) {            c2 = str_data.charCodeAt(i+1);
				tmp_arr[ac++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
				i += 2;
			} else {
				c2 = str_data.charCodeAt(i+1);            c3 = str_data.charCodeAt(i+2);
				tmp_arr[ac++] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}
		} 
		return tmp_arr.join('');
	}
/**
 * String#utf8_encode() -> String
 * 
 * Cette méthode convertie une chaine encodé en ISO-8859-1 vers une chaine encodé en UTF-8.
 *
 * Auteur de la méthode Webtoolkit.info (<a href="http://www.webtoolkit.info/">http://www.webtoolkit.info/</a>)
 **/
	function utf8_encode ( argString ) {

	 
		var utftext = "";
		var start, end;
		var stringl = 0; 
		start = end = 0;
		stringl = string.length;
		for (var n = 0; n < stringl; n++) {
			var c1 = string.charCodeAt(n);        var enc = null;
	 
			if (c1 < 128) {
				end++;
			} else if (c1 > 127 && c1 < 2048) {            enc = String.fromCharCode((c1 >> 6) | 192) + String.fromCharCode((c1 & 63) | 128);
			} else {
				enc = String.fromCharCode((c1 >> 12) | 224) + String.fromCharCode(((c1 >> 6) & 63) | 128) + String.fromCharCode((c1 & 63) | 128);
			}
			if (enc !== null) {            if (end > start) {
					utftext += string.substring(start, end);
				}
				utftext += enc;
				start = end = n+1;        }
		}
	 
		if (end > start) {
			utftext += string.substring(start, string.length);    }
	 
		return utftext;
	}
/**
 * String#replaceAll(search, replace) -> String
 * - search (String): La valeur à chercher, autrement connue comme le masque. Le masque peut être une expression régulière.
 *
 * Remplace toutes les occurrences dans une chaîne.
 * String#replaceAll retourne une chaîne, dont toutes les occurrences de search dans subject ont été remplacées par replace.
 **/
	function replaceAll($search, $replace){
		if(Object.isString($search)){
			return this.replace(new RegExp($search, 'g'), $replace);
		}
	}
/**
 * String#md5([maxlength]) -> String
 * - maxlength (string): Taille maximal de la chaine retourné.
 *
 * Cette méthode encode la chaine au format MD5.
 **/
	function md5(maxlength){
		maxlength = maxlength || this.length;
		
		var strmd5 = hex_md5(this);
		return strmd5.slice(0, maxlength);
	}
	
	return	{	
				addslashes:				addslashes,
				format:					format,
				html_entity_decode : 	html_entity_decode,
				htmlEntities: 			htmlEntities,
				insert:					insert,
				isCodePostal:			isCodePostal,
				isDate:					isDate,
				isHexa:					isHexa,
				isMail:					isMail,
				isNumber:				isNumber,
				isSiret:				isSiret,
				isSiren:				isSiren,
				isTel:					isTel,
				ltrim:					ltrim,
				padLeft:				padLeft,
				padRight:				padRight,
				replaceAll:				replaceAll,
				remove:					remove,
				rtrim:					rtrim,
				sanitize:				sanitize,
				stripslashes: 			stripslashes,
				mui:					mui,
				trim:					trim,
				toDate:					toDate,
				toDecimal:				toDecimal,
				toHexa:					toHexa,
				toMoney:				toMoney,
				toNumber:				toNumber,
				unformat:				unformat,
				utf8_decode:			utf8_decode,
				utf8_encode:			utf8_encode,
				md5:					md5
			};
})());
/** section: lang
 * class Number
 * Extends apporte peu de nouvelle méthode à la Classe Number de Javascript puisque la majorité
 * sont déjà implémenté ou disponible au travers de la classe String.
 **/
Object.extend(Number.prototype, (function(){
/** alias of: String#toMoney
 * Number#toMoney() -> String
 *
 * Transforme la chaine au format Money pour l'affichage de feuille de Calcul.
 **/
	function toMoney(){
		return (""+this).toMoney();
	}
/**
 * Number#toHexa() -> String
 *
 * Cette méthode tente de convertire une chaine décimal en héxadecimal.
 **/
	function toHexa(){
		var oDec = this;
		if(oDec == undefined) return false;
		
		var regDec = /^\d+$/;
	
		if (!regDec.test(oDec)) return false;
		
		return parseInt(oDec,10).toString(16).toUpperCase();
	}
/**
 * Number#format(decimals, decpoint, thousandssep) -> String
 * - decimals (Number): Définit le nombre de décimal.
 * - decpoint (String): Définit le séparateur pour le point décimal.
 * - thousandssep (String): Définit le séparateur des milliers. Seul le premier caractère du paramètre `thousandssep` est utilisé. Par exemple, si vous utilisez bar comme séparateur de milliers, sur le nombre 1000, `Number#format()` retournera 1b000.
 *
 * Formate un nombre pour l'affichage.
 * 
 * #### Les paramètres
 * 
 * Cette méthode accepte un, deux, ou quatre paramètres (et pas trois) :
 * 
 * * Si seul le paramètre number est donné, il sera formaté sans partie décimale, mais avec une virgule entre chaque millier.
 * * Si les deux paramètres number et decimals sont fournis, number sera formaté avec decimals décimales, un point (".") comme séparateur décimal et une virgule entre chaque millier.
 * * Avec quatre paramètres, number sera formaté avec decimals décimales, `decpoint` comme séparateur décimal, et `thousandssep` comme séparateur de milliers.
 *
 **/
	function format(decimals, dec_point, thousands_sep) {
		var n = this, prec = decimals;

		var toFixedFix = function (n,prec) {
			var k = Math.pow(10,prec);
			return (Math.round(n*k)/k).toString();
		};
		
		n = !isFinite(+n) ? 0 : +n;
		prec = !isFinite(+prec) ? 0 : Math.abs(prec);
		var sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep;
		var dec = (typeof dec_point === 'undefined') ? '.' : dec_point;

		var s = (prec > 0) ? toFixedFix(n, prec) : toFixedFix(Math.round(n), prec); //fix for IE parseFloat(0.55).toFixed(0) = 0;

		var abs = toFixedFix(Math.abs(n), prec);
		var _, i;

		if (abs >= 1000) {
			_ = abs.split(/\D/);
			i = _[0].length % 3 || 3;

			_[0] = s.slice(0,i + (n < 0)) +
			_[0].slice(i).replace(/(\d{3})/g, sep+'$1');
			s = _.join(dec);
		} else {
			s = s.replace('.', dec);
		}

		var decPos = s.indexOf(dec);
		if (prec >= 1 && decPos !== -1 && (s.length-decPos-1) < prec) {
			s += new Array(prec-(s.length-decPos-1)).join(0)+'0';
		} else if (prec >= 1 && decPos === -1) {
			s += dec+new Array(prec).join(0)+'0';
		}
		return s;
	}
	return {
		toMoney:	toMoney,
		toHexa:		toHexa,
		format:		format
	};
})());

/** section: lang
 * class Date
 * L'objet Date permet de travailler avec toutes les variables qui concernent les dates et la gestion du temps. Il s'agit d'un objet inclus de façon native dans Javascript, 
 * et que l'on peut toujours utiliser. 
 *
 * `Extends` ajoute un important de méthode utile pour travailler sur les dates. Bon nombre de méthode de `Date` sont issues de PHP et d'autre langage objet afin de faciliter
 * la vie du développeur.
 **/
Object.extend(Date.prototype,(function(){
	var __class__ = 'date';
/**
 * Date#clone() -> Date
 *
 * Créer une copie de l'objet `Date`.
 **/
	function clone(){
		return this.toString_('datetime', 'eng').toDate();	
	}
/**
 * Date#dateDiff(date) -> Number
 * - date (Date): date permettant de calculer la différence entre deux dates.
 *
 * Cette méthode calcule le nombre de jour entre la date l'instance et la date passé en paramètre.
 **/
	function dateDiff(date){
		
		if(this.format('Ymd') == date.format('Ymd')){
			return 0;	
		}
		
		return (Math.abs((this.getTime() - this.getTimezoneOffset()) - (date.getTime() - date.getTimezoneOffset()))/ (1000*60*60*24));
	}
/**
 * Date#daysInMonth() -> Number
 *
 * Retourne le nombre de jours dans le mois de l'instance.
 **/
	function daysInMonth(){
		return this.getArrayDaysByMonth()[this.getMonth()];
	}
	//ignore
	function getDaysInMonth(){
		return this.getArrayDaysByMonth()[this.getMonth()];
	}
/**
 * Date#format(format) -> String
 * - format (String): formatFormat accepté par la fonction PHP date().
 *
 * Retourne la date de l'instance au format demandé.
 * 
 * <p class="note">Voir la description de la fonction date <a href="http://www.php.net/manual/fr/function.date.php">http://www.php.net/manual/fr/function.date.php</a></p>
 *
 * Cette méthode supporte quelques caractères spéciaux en plus que celle prise en charge par `date()` de PHP, dont:
 * 
 * * `$s` : Sera convertie par le jour de début de la semaine de l'instance.
 * * `$e` : Sera convertie par le jour de fin de la semaine de l'instance.
 * * `$s` : Sera convertie par la mois de début de la semaine de l'instance.
 * * `$e` : Sera convertie par la mois de fin de la semaine de l'instance. 
 * * `$b` : Sera convertie par la mois (sur trois lettre) de début de la semaine de l'instance.
 * * `$l` : Sera convertie par la mois (sur trois lettre) de fin de la semaine de l'instance.
 *
 **/
	function format(frm){ 
			
		var str = '';
		
		for(var i = 0; i < frm.length; i += 1){
			var char = frm.substring(i,i+1);

			switch(char){
				case '$':
					char += frm.substring(i+1,i+2);
					if('$s' == char){
						str += this.startDate().getDate();
						i++;
					}
					if('$e' == char){
						str += this.endDate().getDate();
						i++;	
					}

					if('$S' == char){
						str += this.startDate().getDate() + ' ' ;
						str += MUI.getMonth(this.getMonth());
						i++;
					}
					if('$E' == char){
						str += this.endDate().getDate() + ' ';
						str += MUI.getMonth(this.getMonth());
						i++;	
					}
					
					if('$b' == char){
						str += this.startDate().getDate() + ' ' ;
						str += MUI.getMonth(this.getMonth()).slice(0,3);
						i++;
					}
					if('$l' == char){
						str += this.endDate().getDate() + ' ';
						str += MUI.getMonth(this.getMonth()).slice(0,3);
						i++;	
					}
					break;
				//année
				case 'Y':
					str += this.getFullYear();
					break;
				case 'y':
					str += this.getYear();
					break;
					
				//mois
				case 'm':
					str += ('0' + (this.getMonth() + 1)).slice(-2);
					break;
				case 'M':
					str += MUI.getMonth(this.getMonth()).slice(0,3);
					break;
				case 'F':
					str += MUI.getMonth(this.getMonth());
					break;
				case 'n':
					str += (this.getMonth() + 1);
					break;
				case 't':
					str += this.getDaysInMonth();
					break;
					
				//jours
				case 'd':
					str += ('0' + this.getDate()).slice(-2);
					break;
					
				case 'j':
					str += this.getDate();
					break;
					
				case 'D':
					str += MUI.getDay(this.getDay()).slice(0,3);
					break;
					
				case 'l':
					str += MUI.getDay(this.getDay());
					break;
					
				
				//semaine
				case 'w':
					str += $MUI('Sem.') + ' ' +this.getWeekNumber();
					break;
				case 'W':
					str += $MUI('Semaine') + ' ' +this.getWeekNumber();
					break;
				//heure
				case 'h':
					str += ('0' + this.getHours()).slice(-2);
					break;
				case 'i':
					str += ('0' + this.getMinutes()).slice(-2);
					break;
				case 's':
					str += ('0' + this.getSeconds()).slice(-2);
					break;
					
				case '\\':
					i++;
					str += frm.substring(i,i+1);
					break;
					
				default: str+= char;
			}

		}
		
		
		return str;
	}
/**
 * Date#getArrayDaysByMonth() -> Array
 *
 * Retourne un tableau de jour par mois courant à la date
 **/
	function getArrayDaysByMonth(){
		//On initialise le nombre de jour par mois
		var nbJoursfevrier = (this.getFullYear() % 4) == 0 ? 29 : 28;
		//Initialisation du tableau indiquant le nombre de jours par mois
		return new Array(31,nbJoursfevrier,31,30,31,30,31,31,30,31,30,31);
	}
/**
 * Date#getWeekNumber() -> Number
 * 
 * Cette méthode retourne le numéro de la semaine en fonction de l'instance.
 **/
	function getWeekNumber(){

		var NumSemaine = 	0;//numéro de la semaine
		var DebutAn = 		new Date(this.getFullYear(),0,1);
		// calcul du nombre de jours écoulés entre le 1er janvier et la date à traiter.
		// ----------------------------------------------------------------------------
		// initialisation d'un tableau avec le nombre de jours pour chaque mois
		ListeMois = 		this.getArrayDaysByMonth();
		// on parcours tous les mois précédants le mois à traiter 
		// et on calcul le nombre de jour écoulé depuis le 1er janvier dans TotalJour
		var TotalJour = 0;
		for(cpt = 0; cpt < this.getMonth(); cpt++){TotalJour+=ListeMois[cpt];}
		TotalJour += this.getDate();
		
		
		//on determine ensuite le jour correspondant au 1er janvier
		//de 1 pour un lundi à 7 pour un dimanche/

		var JourDebutAn = DebutAn.getDay();
		
		if(JourDebutAn == 0){
			JourDebutAn = 7;
		};
		
		//Calcul du numéro de semaine
		//----------------------------------------------------------------------
		//on retire du TotalJour le nombre de jours que dure la première semaine 
		TotalJour -= 8 - JourDebutAn;
		//on comptabilise cette première semaine
		NumSemaine = 1;
		//on ajoute le nombre de semaine compléte (sans tenir compte des jours restants)
		NumSemaine += Math.floor(TotalJour/7);
		// s'il y a un reste alors le n° de semaine est incrémenté de 1
		if(TotalJour % 7 != 0){
			NumSemaine += 1;
		}

		return(NumSemaine);
	}
/**
 * Date#startDate() -> Date
 *
 * Retourne la date du début de la semaine de l'instance.
 **/
	function startDate(){
		var day = 	this.getDay() == 0 ? 6 : this.getDay() -1;
		var date = 	this.clone();
		
		date.setDate(this.getDate() - day);
		
		return date;
	}
/**
 * Date#endDate() -> Date
 *
 * Retourne la date de fin de la semaine de l'instance.
 **/
	function endDate(){
		var day = 	this.getDay() == 0 ? 6 : this.getDay() -1;
		
		var date = 	this.clone();
		
		date.setDate(this.getDate() + 6 - day);
		
		return date;
	}
/**
 * Date#hoursDiff(date) -> Number
 * - date (Date): date pour le calcul de la différence.
 *
 * Retourne la différence entre deux dates en heure.
 **/
	function hoursDiff(date){
		return (Math.abs(this.getTime() - date.getTime())/ (1000*60*60));
	}
/**
 * Date#hoursDiffWithInterval(interval) -> Number
 * - interval (Array): Bornes.
 *
 * Retourne l'heure entres deux bornes.
 *
 * ##### Exemples
 *
 * * Si l'heure est 23h et que l'intervalle est `[6,18]`, la valeur retourné sera `6 - 18 = 12`.
 * * Si l'heure est 14h et que l'intervalle est `[6,18]`, la valeur retourné sera `14 - 6 = 8`.
 *
 **/
	function hoursDiffWithInterval(interval){

		if(Object.isUndefined(interval)){
			interval = [0,23];	
		}

		if(this.getHours() < interval[0]) return 0;
		if(this.getHours() > interval[1]) return interval[1] - interval[0];
		
		return this.getHours() - interval[0];
				
	}
/**
 * Date#setDay(nb) -> Date
 * - nb (Number): Entier qui correspondant au jour de la semain entre 0 et 6.
 *
 * Permet de fixer la valeur du jour de la semaine.
 **/
	function setDay(nb){
		
		if(Object.isUndefined(nb)) nb = 1;
		
		var currentDay = this.getDay();
		
		currentDay = 	currentDay == 0 ? 6 : currentDay - 1;
		nb = 			nb == 0 ? 6 : nb - 1;

		var days = Math.abs(currentDay - nb);

		if(currentDay > nb) this.setDate(this.getDate() - days);
		else this.setDate(this.getDate() + days);
				
		return this;			
	}
/**
 * Date#timeDiff(date) -> Number
 * - date (Date): date pour le calcul de la différence.
 *
 * Retourne la différence entre deux dates en milisecondes.
 **/
	function timeDiff(date){
		return (Math.abs(this.getTime() - date.getTime()));
	}
	/*
	 * @ignore
	 */
	function toDate(){
		return this;	
	}
/**
 * Date#toString_(type [, lang]) -> String
 * - type (String): type de conversion.
 * - lang (String): format de sortie, Anglais (`eng`) ou Français (`fr`).
 * 
 * Effectue une conversion de l'instance `Date` vers [[String]] au format anglais ou français.
 * Cette méthode est utile pour l'envoi de la date vers une base de données SQL.
 * 
 * #### Paramètre type
 *
 * Il existe trois type de conversion de la date :
 *
 * * `date` : La chaine de sortie sera une date au format (fr) `jj/mm/aaaa` ou (eng) `aaaa-mm-jj`.
 * * `datetime` : La chaine de sortie sera une date au format (fr) `jj/mm/aaaa hh:mm:ss` ou (eng) `aaaa-mm-jj hh:mm:ss`.
 * * `hours` : La chaine de sortie sera une date au format `hh:mm:ss`.
 *
 **/
	function toString_(){

		var args = $A(arguments);
		var str = '';
		if(Object.isUndefined(args[0])) args[0] = '';
		
		switch(args[0]){
			default: 		str = this.toString();
							break;
			case 'datetime':str = this.toStringDateTime(args[1]);
							break;
			case 'date':	str = this.toStringDate(args[1]);
							break;
			case 'hours':	str = this.toStringHours(args[1]);
		}

		return str;
	}
/** deprecated
 * Date#toStringFormated(lang) -> String
 * - lang (String): format de sortie en fonction de langue.
 *
 * Depuis la version 0.9.6 cette méthode est déprecié.
 * 
 * Utilisez plutot la méthode [[Date#toStringDateTime]].
 **/
	function toStringFormated(frm){
		return this.toStringDateTime(frm);
	}
/** 
 * Date#toStringDateTime(frm) -> String
 * - lang (String): format de sortie en fonction de langue (`fr`) ou (`eng`).
 *
 * Convertie l'instance en [[String]] au format (fr) `jj/mm/aaaa hh:mm:ss` ou (eng) `aaaa-mm-jj hh:mm:ss`.
 *
 **/
	function toStringDateTime(frm){
		if(!Object.isUndefined(frm)) frm = frm.toLowerCase();
		
		switch(frm){
			default:
			case 'eng':
				return this.format('Y-m-d h:i:s');
			case 'fr':
				return this.format('d/m/Y h:i:s');
		}
	}
/** 
 * Date#toStringDate(frm) -> String
 * - lang (String): format de sortie en fonction de langue (`fr`) ou (`eng`).
 *
 * Convertie l'instance en [[String]] au format (fr) `jj/mm/aaaa` ou (eng) `aaaa-mm-jj`.
 **/
	function toStringDate(frm){
		if(Object.isUndefined(frm)) frm = 'eng';
		frm = frm.toLowerCase();
		
		switch(frm){
			default:
			case 'eng':
				return this.format('Y-m-d');
				break;
			case 'fr':
				return this.format('d/m/Y');
		}	
	}
/** 
 * Date#toStringHours() -> String
 *
 * Convertie l'instance en [[String]] au format `hh:mm:ss`.
 **/
	function toStringHours(){
		return this.format('h:i:s');
	}

	return {
		__class__:				__class__,
		clone:					clone,
		dateDiff:				dateDiff,
		daysInMonth:			daysInMonth,
		format:					format,
		endDate:				endDate,
		getArrayDaysByMonth:	getArrayDaysByMonth,
		getDaysInMonth:			getDaysInMonth,
		getWeekNumber:			getWeekNumber,
		hoursDiff:				hoursDiff,
		hoursDiffWithInterval:	hoursDiffWithInterval,
		setDay:					setDay,
		startDate:				startDate,
		timeDiff:				timeDiff,
		toDate:					toDate,
		toString_:				toString_,
		toStringDate:			toStringDate,
		toStringDateTime:		toStringDateTime,
		toStringFormated:		toStringFormated,
		toStringHours:			toStringHours
	};
})());
/** section: DOM
 * document
 * L'objet document est probablement l'un des objets les plus importants du modèle objet javascript.
 * Il permet d'accéder à tous les éléments affichés sur la page, de contrôler les saisies, de modifier l'apparence et le contenu.
 **/

/**
 * document.navigator
 * Cette namespace permet de controler le type et la version du navigateur pour vos applications.
 **/
document.navigator = {
/**
 * document.navigator.client -> String
 * Contient le nom du navigateur en cours.
 **/
	client: 'undefined',
/**
 * document.navigator.version -> String
 * Contient la version du navigateur en cours.
 **/
	version:'undefined',
/**
 * document.navigator.initialize() -> void
 *
 * Initialise les paramètres de document.navigator.
 **/
	initialize:function(){
		var obj = Prototype.Browser;
	
		if(obj.IE){
			/MSIE (\d+\.\d+);/.test(navigator.userAgent);
			this.client = 'IE';
			var ieversion=new Number(RegExp.$1);
			if (ieversion>=8) this.version = 8;
			else if (ieversion>=7) this.version = 7;
			else if (ieversion>=6) this.version = 6;
			else if(ieversion>=5) this.version = 5;
			return;
		}
		
		if(obj.Gecko){
			
			/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent);
			this.client = 'Firefox';
			var ffversion = new Number(RegExp.$1);
			if(ffversion >= 3) this.version = 3;
			else if(ffversion >= 2) this.version = 2;
			else if(ffversion >= 1) this.version = 1;
			return;
		}
		
		if(obj.Webkit){
			//Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent);
			this.client = 'Safari';
			 /*var ffversion = new Number(RegExp.$1);
			 if(ffversion >= 3) this.version = 3;
			 else if(ffversion >= 2) this.version = 2;
			 else if(ffversion >= 1) this.version = 1;*/
			return;
		}
		
		if(obj.Opera){
	
			/Opera[\/\s](\d+\.\d+)/.test(navigator.userAgent);//test for Opera/x.x or Opera x.x (ignoring remaining decimal places);
			var oprversion = new Number(RegExp.$1); // capture x.x portion and store as a number
			this.client = 'Opera';
			if (oprversion >= 10) this.version = 10;
			else if(oprversion >= 9) this.version = 9;
			else if(oprversion >= 8) this.version = 8;
			else if(oprversion >= 7) this.version = 7;
			return;
		}
		
		if(/Chrome[\/\s](\d+\.\d+)/.test(navigator.userAgent)){
			var chromeversion = new Number(RegExp.$1); // capture x.x portion and store as a number
			this.client = 'Chrome';
			
			if(chromeversion >= 2) this.version = 2;
			else if(chromeversion >= 1) this.version = 1;
		}
	},
/**
 * document.navigator.parseGet() -> Array
 *
 * Parse le champs GET de l'URL qui execute le script et retourne le tableau associatif des paramètres.
 **/
	parseGet:function(){
		var string = window.location.search;
		string = string.substr(1, string.length-1);
		var tab = string.split('&');
		$_GET = {};
		for(var i = 0; i < tab.length; i++){
			var tmp = tab[i].split('=');
			$_GET[tmp[0]] = tmp[1];
		}
		return $_GET;
	},
/**
 * document.navigator.S_GET(key) -> String
 * - key(String): La clef recherchée.
 *
 * Retourne la valeur de la clef contenu dans le tableau $_GET.
 *
 * <p class="note">Le nom de la méhode est document.navigator.$_GET(key). Le parser PDOC pose problème avec le caractère $.</p>
 *
 **/
	$_GET: function(key){
		return $_GET[key];
	}
};
/**
 * document.stage
 * Donne les informations sur la stage comme la hauteur et la largeur
 * de la scène, c'est-à-dire la partie visible de la page HTML.
 **/
document.stage = {
/**
 * document.stage.body -> Element
 * Cette attribut est une référence sur la balise body du document HTML.
 **/
	body: '',
/**
 * document.stage.stageHeight -> Number
 * Donne la hauteur du corps visible de la page HTML.
 **/
	stageHeight:0,
/**
 * document.stage.stageWidth -> Number
 * Donne la largeur du corps visible de la page HTML.
 **/
	stageWidth:0,
/**
 * document.stage.getDimensions() -> Object
 * 
 * Donne la dimensions de la scène Body.
 * 
 * ##### Composition de l'objet de retour 
 *	
 *     {
 *          0: {@link Number} largeur,
 *          1: {@link Number} hauteur,
 *          'height' : {@link Number} height,
 *          'width' : {@link Number} largeur
 *     }
 *
 **/
	getDimensions:function(){
		var div = new Node('div');
		
		div.setStyle({
					 height:	"1px",
					 width:		"100%",
					 position:	"fixed",
					 bottom:	"0",
					 left:		"0",
					 backgroundColor:'black'
					 });
		
		this.body.appendChild(div);
		
		this.stageWidth = 	Element.getWidth(div);
		this.stageHeight = 	Element.cumulativeOffset(div).top;
	
		this.body.removeChild(div);
		
		return {
			'0': this.stageWidth,
			'1': this.stageHeight, 
			height:this.stageHeight, 
			width:this.stageWidth
		};
	}
};
/**
 * document.get_html_translation_table(table, quotestyle) -> Object
 * - table (Array): table de conversion.
 * - quotestyle (String): Type de conversion.
 *
 * Table de translation HTML.
 *
 * Développé par Philip Peterson.
 **/
document.get_html_translation_table = function(table, quote_style) {
	// http://kevin.vanzonneveld.net
	// +   original by: Philip Peterson
	// +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
	// +   bugfixed by: noname
	// +   bugfixed by: Alex
	// +   bugfixed by: Marco
	// +   bugfixed by: madipta
	// +   improved by: KELAN
	// +   improved by: Brett Zamir (http://brett-zamir.me)
	// +   bugfixed by: Brett Zamir (http://brett-zamir.me)
	// +      input by: Frank Forte
	// +   bugfixed by: T.Wild
	// +      input by: Ratheous
	// %          note: It has been decided that we're not going to add global
	// %          note: dependencies to php.js, meaning the constants are not
	// %          note: real constants, but strings instead. Integers are also supported if someone
	// %          note: chooses to create the constants themselves.
	// *     example 1: get_html_translation_table('HTML_SPECIALCHARS');
	// *     returns 1: {'"': '&quot;', '&': '&amp;', '<': '&lt;', '>': '&gt;'}
	
	var entities = {}, hash_map = {}, decimal = 0, symbol = '';
	var constMappingTable = {}, constMappingQuoteStyle = {};
	var useTable = {}, useQuoteStyle = {};
	
	// Translate arguments
	constMappingTable[0]      = 'HTML_SPECIALCHARS';
	constMappingTable[1]      = 'HTML_ENTITIES';
	constMappingQuoteStyle[0] = 'ENT_NOQUOTES';
	constMappingQuoteStyle[2] = 'ENT_COMPAT';
	constMappingQuoteStyle[3] = 'ENT_QUOTES';
 
	useTable       = !isNaN(table) ? constMappingTable[table] : table ? table.toUpperCase() : 'HTML_SPECIALCHARS';
	useQuoteStyle = !isNaN(quote_style) ? constMappingQuoteStyle[quote_style] : quote_style ? quote_style.toUpperCase() : 'ENT_COMPAT';
 
	if (useTable !== 'HTML_SPECIALCHARS' && useTable !== 'HTML_ENTITIES') {
		throw new Error("Table: "+useTable+' not supported');
		// return false;
	}
 
	entities['38'] = '&amp;';
	if (useTable === 'HTML_ENTITIES') {
		entities['160'] = '&nbsp;';
		entities['161'] = '&iexcl;';
		entities['162'] = '&cent;';
		entities['163'] = '&pound;';
		entities['164'] = '&curren;';
		entities['165'] = '&yen;';
		entities['166'] = '&brvbar;';
		entities['167'] = '&sect;';
		entities['168'] = '&uml;';
		entities['169'] = '&copy;';
		entities['170'] = '&ordf;';
		entities['171'] = '&laquo;';
		entities['172'] = '&not;';
		entities['173'] = '&shy;';
		entities['174'] = '&reg;';
		entities['175'] = '&macr;';
		entities['176'] = '&deg;';
		entities['177'] = '&plusmn;';
		entities['178'] = '&sup2;';
		entities['179'] = '&sup3;';
		entities['180'] = '&acute;';
		entities['181'] = '&micro;';
		entities['182'] = '&para;';
		entities['183'] = '&middot;';
		entities['184'] = '&cedil;';
		entities['185'] = '&sup1;';
		entities['186'] = '&ordm;';
		entities['187'] = '&raquo;';
		entities['188'] = '&frac14;';
		entities['189'] = '&frac12;';
		entities['190'] = '&frac34;';
		entities['191'] = '&iquest;';
		entities['192'] = '&Agrave;';
		entities['193'] = '&Aacute;';
		entities['194'] = '&Acirc;';
		entities['195'] = '&Atilde;';
		entities['196'] = '&Auml;';
		entities['197'] = '&Aring;';
		entities['198'] = '&AElig;';
		entities['199'] = '&Ccedil;';
		entities['200'] = '&Egrave;';
		entities['201'] = '&Eacute;';
		entities['202'] = '&Ecirc;';
		entities['203'] = '&Euml;';
		entities['204'] = '&Igrave;';
		entities['205'] = '&Iacute;';
		entities['206'] = '&Icirc;';
		entities['207'] = '&Iuml;';
		entities['208'] = '&ETH;';
		entities['209'] = '&Ntilde;';
		entities['210'] = '&Ograve;';
		entities['211'] = '&Oacute;';
		entities['212'] = '&Ocirc;';
		entities['213'] = '&Otilde;';
		entities['214'] = '&Ouml;';
		entities['215'] = '&times;';
		entities['216'] = '&Oslash;';
		entities['217'] = '&Ugrave;';
		entities['218'] = '&Uacute;';
		entities['219'] = '&Ucirc;';
		entities['220'] = '&Uuml;';
		entities['221'] = '&Yacute;';
		entities['222'] = '&THORN;';
		entities['223'] = '&szlig;';
		entities['224'] = '&agrave;';
		entities['225'] = '&aacute;';
		entities['226'] = '&acirc;';
		entities['227'] = '&atilde;';
		entities['228'] = '&auml;';
		entities['229'] = '&aring;';
		entities['230'] = '&aelig;';
		entities['231'] = '&ccedil;';
		entities['232'] = '&egrave;';
		entities['233'] = '&eacute;';
		entities['234'] = '&ecirc;';
		entities['235'] = '&euml;';
		entities['236'] = '&igrave;';
		entities['237'] = '&iacute;';
		entities['238'] = '&icirc;';
		entities['239'] = '&iuml;';
		entities['240'] = '&eth;';
		entities['241'] = '&ntilde;';
		entities['242'] = '&ograve;';
		entities['243'] = '&oacute;';
		entities['244'] = '&ocirc;';
		entities['245'] = '&otilde;';
		entities['246'] = '&ouml;';
		entities['247'] = '&divide;';
		entities['248'] = '&oslash;';
		entities['249'] = '&ugrave;';
		entities['250'] = '&uacute;';
		entities['251'] = '&ucirc;';
		entities['252'] = '&uuml;';
		entities['253'] = '&yacute;';
		entities['254'] = '&thorn;';
		entities['255'] = '&yuml;';
	}
 
	if (useQuoteStyle !== 'ENT_NOQUOTES') {
		entities['34'] = '&quot;';
	}
	if (useQuoteStyle === 'ENT_QUOTES') {
		entities['39'] = '&#39;';
	}
	entities['60'] = '&lt;';
	entities['62'] = '&gt;';
 
 
	// ascii decimals to real symbols
	for (decimal in entities) {
		symbol = String.fromCharCode(decimal);
		hash_map[symbol] = entities[decimal];
	}
	
	return hash_map;
};
/** section: DOM
 * class Event
 *
 *  <p class="note">Documentation extrait de http://api.prototypejs.org</p>
 * 	<br>
 *  The namespace for Prototype's event system.
 *
 *  ##### Events: a fine mess
 *
 *  Event management is one of the really sore spots of cross-browser
 *  scripting.
 *
 *  True, the prevalent issue is: everybody does it the W3C way, and MSIE
 *  does it another way altogether. But there are quite a few subtler,
 *  sneakier issues here and there waiting to bite your ankle &mdash; such as the
 *  `keypress`/`keydown` issue with KHTML-based browsers (Konqueror and
 *  Safari). Also, MSIE has a tendency to leak memory when it comes to
 *  discarding event handlers.
 *
 *  ##### Prototype to the rescue
 *
 *  Of course, Prototype smooths it over so well you'll forget these
 *  troubles even exist. Enter the `Event` namespace. It is replete with
 *  methods that help to normalize the information reported by events across
 *  browsers.
 *
 *  `Event` also provides a standardized list of key codes you can use with
 *  keyboard-related events, including `KEY_BACKSPACE`, `KEY_TAB`,
 *  `KEY_RETURN`, `KEY_ESC`, `KEY_LEFT`, `KEY_UP`, `KEY_RIGHT`, `KEY_DOWN`,
 *  `KEY_DELETE`, `KEY_HOME`, `KEY_END`, `KEY_PAGEUP`, `KEY_PAGEDOWN` and
 *  `KEY_INSERT`.
 *
 *  The functions you're most likely to use a lot are `Event.observe`,
 *  `Event.element` and `Event.stop` . If your web app uses custom events,
 *  you'll also get a lot of mileage out of `Event.fire`.
 *  
 *  ##### Instance methods on event objects
 *  As of Prototype 1.6, all methods on the `Event` object are now also 
 *  available as instance methods on the event object itself:
 *  
 *  **Before**
 *  
 *      $('foo').observe('click', respondToClick);
 *      
 *      function respondToClick(event) {
 *        var element = Event.element(event);
 *        element.addClassName('active');
 *      }
 *  
 *  **After**
 *  
 *      $('foo').observe('click', respondToClick);
 *      
 *      function respondToClick(event) {
 *        var element = event.element();
 *        element.addClassName('active');
 *      }
 *  
 *  These methods are added to the event object through `Event.extend`,
 *  in the same way that `Element` methods are added to DOM nodes through 
 *  `Element.extend`. Events are extended automatically when handlers are 
 *  registered with Prototype's `Event.observe` method; if you're using a 
 *  different method of event registration, for whatever reason,you'll need to
 *  extend these events manually with `Event.extend`.
 **/
 
 /**
 *  Event.observe(@element, eventName, handler) -> Element
 *  - element (Element | String): The DOM element to observe, or its ID.
 *  - eventName (String): The name of the event, in all lower case, without
 *    the "on" prefix&nbsp;&mdash; e.g., "click" (not "onclick").
 *  - handler (Function): The function to call when the event occurs.
 *	
 *  Registers an event handler on a DOM element. Aliased as `Element#observe`.
 *  <br>
 *  <br>
 *  <p class="note">Documentation extrait de http://api.prototypejs.org</p>
 *  <br>
 *  <br>
 *  `Event.observe` smooths out a variety of differences between browsers
 *  and provides some handy additional features as well. Key features in brief:
 *  * Several handlers can be registered for the same event on the same element.
 *  * Prototype figures out whether to use `addEventListener` (W3C standard) or
 *    `attachEvent` (MSIE); you don't have to worry about it.
 *  * The handler is passed an _extended_ `Event` object (even on MSIE).
 *  * The handler's context (`this` value) is set to the extended element
 *    being observed (even if the event actually occurred on a descendent
 *    element and bubbled up).
 *  * Prototype handles cleaning up the handler when leaving the page
 *    (important for MSIE memory leak prevention).
 *  * `Event.observe` makes it possible to stop observing the event easily
 *    via `Event.stopObserving`.
 *  * Adds support for `mouseenter` / `mouseleave` events in all browsers.
 *
 *  Although you can use `Event.observe` directly and there are times when
 *  that's the most convenient or direct way, it's more common to use its
 *  alias `Element#observe`. These two statements have the same effect:
 *
 *      Event.observe('foo', 'click', myHandler);
 *      $('foo').observe('click', myHandler);
 *
 *  The examples in this documentation use the `Element#observe` form.
 *
 *  ##### The Handler
 *
 *  Signature:
 *
 *      function handler(event) {
 *        // `this` = the element being observed
 *      }
 *
 *  So for example, this will turn the background of the element 'foo' blue
 *  when it's clicked:
 *
 *      $('foo').observe('click', function(event) {
 *        this.setStyle({backgroundColor: 'blue'});
 *      });
 *
 *  Note that we used `this` to refer to the element, and that we received the
 *  `event` object as a parameter (even on MSIE).
 *
 *  ##### It's All About Timing
 *
 *  One of the most common errors trying to observe events is trying to do it
 *  before the element exists in the DOM. Don't try to observe elements until
 *  after the `document.observe dom:loaded` event or `window` `load` event
 *  has been fired.
 *
 *  ##### Preventing the Default Event Action and Bubbling
 *
 *  If we want to stop the event (e.g., prevent its default action and stop it
 *  bubbling), we can do so with the extended event object's `Event#stop`
 *  method:
 *
 *      $('foo').observe('click', function(event) {
 *        event.stop();
 *      });
 *
 *  ##### Finding the Element Where the Event Occurred
 *
 *  Since most events bubble from descendant elements up through the hierarchy
 *  until they're handled, we can observe an event on a container rather than
 *  individual elements within the container. This is sometimes called "event
 *  delegation". It's particularly handy for tables:
 *
 *      language: html
 *      <table id='records'>
 *        <thead>
 *          <tr><th colspan='2'>No record clicked</th></tr>
 *        </thead>
 *        <tbody>
 *          <tr data-recnum='1'><td>1</td><td>First record</td></tr>
 *          <tr data-recnum='2'><td>2</td><td>Second record</td></tr>
 *          <tr data-recnum='3'><td>3</td><td>Third record</td></tr>
 *        </tbody>
 *      </table>
 *
 *  Instead of observing each cell or row, we can simply observe the table:
 *
 *      $('records').observe('click', function(event) {
 *        var clickedRow = event.findElement('tr');
 *        if (clickedRow) {
 *          this.down('th').update("You clicked record #" + clickedRow.readAttribute("data-recnum"));
 *        }
 *      });
 *
 *  When any row in the table is clicked, we update the table's first header
 *  cell saying which record was clicked. `Event#findElement` finds the row
 *  that was clicked, and `this` refers to the table we were observing.
 *
 *  ##### Stopping Observing the Event
 *
 *  If we don't need to observe the event anymore, we can stop observing it
 *  with `Event.stopObserving` or its `Element#stopObserving` alias.
 *
 *  ##### Using an Instance Method as a Handler
 *
 *  If we want to use an instance method as a handler, we will probably want
 *  to use `Function#bind` to set the handler's context; otherwise, the
 *  context will be lost and `this` won't mean what we expect it to mean
 *  within the handler function. E.g.:
 *
 *      var MyClass = Class.create({
 *        initialize: function(name, element) {
 *          this.name = name;
 *          element = $(element);
 *          if (element) {
 *            element.observe(this.handleClick.bind(this));
 *          }
 *        },
 *        handleClick: function(event) {
 *          alert("My name is " + this.name);
 *        },
 *      });
 *
 *  Without the `Function#bind`, when `handleClick` was triggered by the
 *  event, `this` wouldn't refer to the instance and so the alert wouldn't
 *  show the name. Because we used `Function#bind`, it works correctly. See
 *  `Function#bind` for details. There's also `Function#bindAsEventListener`,
 *  which is handy for certain very specific situations. (Normally,
 *  `Function#bind` is all you need.)
 *
 *  ##### Side Notes
 *
 *  Although Prototype smooths out most of the differences between browsers,
 *  the fundamental behavior of a browser implementation isn't changed. For
 *  example, the timing of the `change` or `blur` events varies a bit from
 *  browser to browser.
 *
 *  ##### Changes in 1.6.x
 *
 *  Prior to Prototype 1.6, `Event.observe` supported a fourth argument
 *  (`useCapture`), a boolean that indicated whether to use the browser's
 *  capturing phase or its bubbling phase. Since MSIE does not support the
 *  capturing phase, we removed this argument from 1.6, lest it give users the
 *  false impression that they can use the capturing phase in all browsers.
 *
 *  1.6 also introduced setting the `this` context to the element being
 *  observed, automatically extending the `Event` object, and the
 *  `Event#findElement` method.
 **/
Object.extend(Event, {
/**
 * Event#KEY_F5 -> Number
 * Code clavier de la touche F5.
 **/
	KEY_F5:	116,
/**
 * Event#KEY_F6 -> Number 
 * Code clavier de la touche F6.
 **/
	KEY_F6:	117,
/**
 * Event#KEY_F7 -> Number 
 * Code clavier de la touche F7.
 **/
	KEY_F7:	118,
/**
 * Event#KEY_F8 -> Number 
 * Code clavier de la touche F8.
 **/
	KEY_F8:	119,
/**
 * Event#KEY_F9 -> Number 
 * Code clavier de la touche F9.
 **/
	KEY_F9:	120,
/**
 * Event#KEY_F10 -> Number 
 * Code clavier de la touche F10.
 **/
	KEY_F10:121,
/**
 * Event#KEY_F11 -> Number 
 * Code clavier de la touche F11.
 **/
	KEY_F11:122,
/**
 * Event#KEY_F12 -> Number 
 * Code clavier de la touche F12.
 **/
	KEY_F12:123,
/**
 * Event#wheel(event) -> Number
 * - event (Event): Evenement généré par le navigateur.
 *
 * Ajoute le support de l'evenement MouseWheel à l'objet Event
 *
 **/
	wheel:function (event){
		var delta = 0;
		if (!event) event = window.event;
		if (event.wheelDelta) {
			delta = event.wheelDelta/120; 
			if (window.opera) delta = -delta;
		} else if (event.detail) { delta = -event.detail/3;	}
		return Math.round(delta); //Safari Round
	},
	/**
	 * Event#getKeyCode(event) -> Number
	 * - event (Event) -> Evenement généré par le navigator.
	 *
	 * Extrait et retourne le code du clavier de l'événement.
	 */
	getKeyCode: function(event){
		return (!document.all) ? event.which : event.button;	
	}
});

var $Body = null;
//Alias
try{
	if(!Object.isUndefined(jQuery)){
	/**
	 * $J() -> jQuery
	 *
	 * Alias de la bibliothèque jQuery.
	 **/
		$J = $.noConflict();
	}
}catch(er){}
//--------------------------------------------------------------------------------------------------------
//Execution-----------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------

document.navigator.initialize();
document.navigator.parseGet();

Extends.load();

document.observe('dom:loaded',function(evt){
	$Body = document.stage.body = document.getElementsByTagName("body")[0];
	document.stage.getDimensions();
	
	$Body.getDimensions = 	document.stage.getDimensions.bind(document.stage);
	$Body.getStageHeight =	function(){return document.stage.stageHeight};
	$Body.getStageWidth =	function(){return document.stage.stageWidth};
	
	/*
	 * @ignore
	 */
	window.onresize = function(evt){
		document.stage.getDimensions(evt);
		Extends.fire('resize', evt);
	};
	Extends.fire('dom:loaded', evt);

});